1)定义: 闭包就是能够读取其他函数内部变量的函数。简单说来,可以理解把闭包理解为“定义在一个函数内部的函数”
function f1() { var n = 999; function f2() { console.log(n); } return f2; } var result = f1(); result(); // 999 //1)上述代码中的 f2 函数就是一个闭包。 //2)由于链式作用域的原因,f2 可以访问 f1内定义的变量,反之则不行 //3)把f2作为返回值,就可以在f1外部读取它的内部变量。
2)闭包的用途:
可以读取函数内部的变量。
让这些变量始终保存在内存中。
function f1() { var n = 999; nAdd = function () { n += 1 } function f2() { console.log(n) } return f2; } var result = f1(); result(); // 999 nAdd(); result(); // 1000 //原因:f1中的局部变量n 一直在内存中,并没有在f1调用后被自动清除。 //因为f1是f2的父函数,而f2被赋给了一个全局变量,导致f2始终在内存中,而f2的存在依赖f1,因此f1也始终在内存中,最终都不会再调用结束被垃圾回收机制回收。 //此处注意,nAdd也是一个闭包,它被挂载在全局变量下。
3)使用闭包注意点:
定义: this
是JavaScript 语言的一个关键字,它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。
简单来讲:this 就是函数运行时所在的环境对象,即谁调用函数就指向谁。
案例:
// 1)this 代表全局对象,因为test 调用时挂载在全局对象下,故this代表全局对象 var x = 1; function test() { console.log(this.x); } test(); //1 // 2)对象方法调用, 此时this指向 obj,因为调用时是obj 在调用 function test() { console.log(this.x); } var obj = {} obj.x = 1; obj.m = test; obj.m(); // 1 // 3)构造函数调用, new时生成一个新对象,此时this就指向新对象 var x = 2; function test() { this.x = 1; } var obj = new test();// this会指向新对象,不会指向全局对象 console.log(x); // 2
apply调用: (apply
是函数的一个方法,作用是改变函数的调用对象)
用法:apply(thisObj, [args]); // [args] 是一个数组或类数组,是一个参数列表 //示例如下: var x = 0; function test() { console.log(this.x); } var obj = {}; obj.x = 1; obj.m = test; //apply无参,则代表指向全局对象 obj.m.apply()// 0 //指定绑定this到obj上 obj.m.apply(obj) //1
call调用改变this
用法: call(thisObj, arg1, arg2, arg3, arg4); //示例代码: var name = "Jerry"; var obj = { name: "Tom" }; function sayName() { return this.name; } console.log(sayName.call(this));//Jerry console.log(sayName.call(obj));//Tom
typeof
和 instanceof
常用来判断一个变量是否为空,或者什么类型的定义和用法:typeof返回值是一个字符串,用来说明变量的数据类型。
var a = 1; typeof a // "number"
细节:
- typeof 一般只能返回:number boolean string function object undefined - 对于Array, null 等特殊对象,使用 typeof 一律返回 object,这正是它的局限性。
定义:instanceof用于判断一个变量是否属于某个对象的实例。
var a = new Array(); console.log(a instanceof Array); //true console.log(a instanceof Object);// true,因为 Array 是 Object 的子类 function test() { } var a = new test(); console.log(a instanceof test); //true
//继承方式,原型链形式,将方法挂载在 prototype 对象上 function teacher(name) { this.name = name; } teacher.prototype.sayName = function () { console.log("name is" + this.name) } var teacher1 = new teacher("xiaoming"); teacher1.sayName(); //子类继承父类 function student(name) { this.name = name; } student.prototype = new teacher(); var student1 = new student("xiaohong"); student1.sayName();
function teacher(name, age) { this.name = name; this.age = age; this.sayHi = function () { console.log('Name:' + name + ", Age:" + age) }; } function student() { var args = arguments; teacher.call(this, args[0], args[1]); } var teacher1 = new teacher('xiaoming', 30); teacher1.sayHi(); var student1 = new student('xiaolan', 10); student1.sayHi();
prototype, call / apply
)function teacher(name, age) { this.name = name; this.age = age; } teacher.prototype.sayName = function () { onsole.log('name:' + this.name); } teacher.prototype.sayAge = function () { console.log('age:' + this.age); } function student() { var args = arguments; teacher.call(this, args[0], args[1]); } student.prototype = new teacher(); var student1 = new student('xiaolin', 23); student1.sayName(); student1.sayAge();
var a = "42"; var b = Number( a ); a; // "42" b; // 42 -- 是个数字!
var a = "42"; var b = a * 1; // "42" 隐式转型成 42 a; // "42" b; // 42 -- 是个数字!
(1)严格比较 (如===
)在不允许强制转型的情况下检查两个值是否相等
(2)抽象比较 (如==
)在允许强制转型的情况下检查两个值是否相等
var a = "42"; var b = 42; a == b; // true a === b; // false
说明: use strict
出现在JS代码的顶部,可以帮助你写出更安全的JS代码。如果你错误的创建了全局变量,他会抛出错误的方式来警告你
例如:如下代码因为x 没有被定义,并使用了全局作用域中的某个值对其进行赋值,加了 use strict 后就会报错。
function doSomething(val) { "use strict"; x = val + 10; }
将变量声明到函数内部,严格模式下不会报错,如下:
function doSomething(val) { "use strict"; var x = val + 10; }
首先声明: 他们都是JS的两种类型。
尚未初始化:undefined
空值:null
注意:null
和 undefined
是两个不同的对象,如下:
null == undefined // true null === undefined // false
说明:delete 操作符,只适用于删除对象的属性。
var a = {b:1} delete a.b; console.log(a) //object {}
如果想要删除对象:
a = null;
delete 删除变量不可行
var name = 'lily'; delete name; console.log(name); //lily
delete 删除不了原型链中的变量
fun.prototype.age = 18; delete obj.age; console.log(obj.age) //18
对象字面量法:
var obj = {}
工厂模式:
function Person(name, age) { var o = new Object(); o.name = name; o.age = age; o.say = function () { console.log(name); } return o; }
构造函数法:
//1 直接new Object 对象 var obj = new Object(); //构造函数 function Person(name, age) { this.name = name; this.age = age; this.say = function () { console.log(name); } } var person = new Person('hello', 18);
原型模式:
//实现方法与属性共享,可以动态添加对象的属性和方法。但是没法创建实例自己的属性和方法,也没办法传递参数 function Person() { Person.prototype.name = 'jerry'; Person.prototype.say = function () { console.log(this.name) } Person.prototype.friends = ['friend1']; } var person = new Person();
构造函数和原型结合:
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.say = function () { console.log(this.name) } var person = new Person('hello');
网站优化的思路:
对普通的网站有一个统一的思路,就是尽量向前端优化、减少数据库操作、减少磁盘IO。向前端优化指的是,在不影响功能和体验的情况下, 能在浏览器执行的不要在服务端执行,能在缓存服务器上直接返回的不要到应用服务器,程序能直接取得的结果不要到外部取得, 本机内能取得的数据不要到远程取,内存能取到的不要到磁盘取,缓存中有的不要去数据库查询。 减少数据库操作指减少更新次数、缓存结果减少查询次数、将数据库执行的操作尽可能的让你的程序完成(例如join查询),减少磁盘IO指尽量不使用文件系统作为缓存、减少读写文件次数等。 程序优化永远要优化慢的部分,换语言是无法“优化”的。
1)定义和用法: 垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程中使用的内存。
2)原理: 垃圾收集器会定期(周期性)找出那些不再继续使用的变量,然后释放其内存,但是这个过程不是实时的,因为开销比较大,所以GC会按固定时间间隔周期执行。
function fn1() { var obj = { name: "jerry", age: 10 }; } function fn2() { var obj = { name: "jerry", age: 10 }; return obj; } var a = fn1();// fn1 方法中的局部变量会被GC回收 var b = fn2();// fn2 中的局部变量不会被GC回收,因为 obj 被挂载在了全局变量上
3)垃圾回收策略: 标记清除(较为常用)和引用计数
定义和用法:当变量进入环境时,将变量标记"进入环境",当变量离开环境时,标记为:"离开环境"。 某一个时刻,垃圾回收器会过滤掉环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。 到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
定义和用法:引用计数是跟踪记录每个值被引用的次数。 基本原理:就是变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象。
4)内存管理
1、什么时候触发垃圾回收? 垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。 IE6的垃圾回收是根据内存分配量运行的,当环境中的变量,对象,字符串达到一定数量时触发垃圾回收。垃圾回收器一直处于工作状态,严重影响浏览器性能。 IE7中,垃圾回收器会根据内存分配量与程序占用内存的比例进行动态调整,开始回收工作。 2、合理的GC方案:(1)、遍历所有可访问的对象; (2)、回收已不可访问的对象。 3、GC缺陷:(1)、停止响应其他操作; 4、GC优化策略:(1)、分代回收(Generation GC);(2)、增量GC
定义: 由于浏览器同源策略,凡是发送请求的URL 协议
、域名
、端口
三者之间任意一与前页面地址不同则为跨域。
存在跨域的情况
- 网络协议不同,如http协议访问https协议 - 端口不同,如80端口访问8080端口 - 域名不同,如taobao.com 访问 baidu.com - 子域名不同,如 abc.baidu.com 访问 def.baidu.com - 域名和域名对应ip,如www.a.com 访问 10.0.0.8
(1)proxy代理
定义和用法:proxy代理用于将请求发送给后台服务器,通过服务器来发送请求,然后将请求结果传回前端。 实现方法:通过nginx 代理。 注意点:1、如果你代理的是https协议的请求,那么你的proxy首先需要信任该证书(尤其是自定义证书)或者忽略证书检查,否则你的请求无法成功。
(2)CORS 【Cross-Origin Resource Sharing】
定义和用法:是现代浏览器支持跨域资源请求的一种最常用的方式。
使用方法: 使用方法:一般需要后端人员在处理请求数据的时候,添加允许跨域的相关操作。如下:
res.writeHead(200, { "Content-Type": "text/html; charset=UTF-8", "Access-Control-Allow-Origin":'http://localhost', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type' });
(3)jsonp
定义和用法:通过动态插入一个script标签。浏览器对script的资源引用没有同源限制,同时资源加载到页面后会立即执行。
特点:通过动态创建script来读取他域的动态资源,获取的数据一般为json格式。
<script> function testjsonp(data) { console.log(data.name); // 获取返回的结果 } </script> <script> var _script = document.createElement('script'); _script.type = "text/javascript"; _script.src = "http://localhost:8888/jsonp?callback=testjsonp"; document.head.appendChild(_script); </script>
缺点:1、这种方式无法发送post请求。2、另外要确定jsonp的请求是否失败并不容易,大多数框架实现都是结合超时时间来判定的。
1、JavaScript 当中,如果检测一个变量时一个String 类型?
typeof(obj) === "string" typeof obj === "string" obj.constructor === String
2、请写出3个使用this的典型应用
在HTML 元素事件属性上使用
<input type="button" onclick="showInfo(this);" value=”点击一下”/>
构造函数
function Animal(name, color) { this.name = name; this.color = color; }
apply() / call()求数组最值
var numbers = [5, 458 , 120 , -215 ]; var maxInNumbers = Math.max.apply(this, numbers); console.log(maxInNumbers); // 458 var maxInNumbers = Math.max.call(this,5, 458 , 120 , -215); console.log(maxInNumbers); // 458
3、请用JS 去除字符串中的空格
方法一:使用replace正则匹配的方法
去除所有空格: str = str.replace(/\s*/g,""); 去除两头空格: str = str.replace(/^\s*|\s*$/g,""); 去除左空格: str = str.replace( /^\s*/, ""); 去除右空格: str = str.replace(/(\s*$)/g, "");
方法二:使用str.trim()方法
var str = " xiao ming "; var str2 = str.trim(); // xiao ming,str.trim() 的局限性,无法去除中间的空格
4、如何获取浏览器URL中查询字符串中的参数
思路:使用 window.location.href
,通过 split
方法根据 &
进行分割获取。
function showWindowHref() { var sHref = window.location.href; var args = sHref.split('?'); if (args[0] == sHref) { return ""; } var arr = args[1].split('&'); var obj = {}; for (var i = 0; i < arr.length; i++) { var arg = arr[i].split('='); obj[arg[0]] = arg[1]; } return obj; } var href = showWindowHref(); // obj console.log(href['name']); // xiaoming
5、解释事件冒泡以及如何阻止它?
event.cancelBubble
或 event.stopPropagation()
(低于IE9)6、如何检查一个数字是否为正数?
思路:对1进行取模,看是否有余数。
function isInit(num) { return num % 1 === 0; } console.log(isInit(4));//true console.log(isInit(12.2));//false console.log(isInit(0.3));//false
7、解释JS "undefined" 和 "not defined"之间的区别
aaa // Uncaught ReferenceError: aaa is not defined typeof aaa// "undefined"
var x; // 声明 typeof x === 'undefined' // true
8、匿名函数和命名函数有什么区别?
//(1)赋给变量foo 的匿名函数 var foo = function () { } //(2)赋给变量x 的命名函数 var x = function bar() { }
9、如何在JS中创建私有变量?
将变量创建成局部变量,就是这个函数被调用,也无法在函数之外访问这个变量
function func() { var priv = "secret code"; } console.log(priv)// 抛异常 Uncaught ReferenceError: priv is not defined
要访问私有变量,需要借助闭包函数
//要想访问这个私有变量,可以通过闭包的方式,将闭包函数返回 function func() { var priv = "secret code"; return function () { return priv; } } var getPriv = func(); getPriv();//"secret code"
10、以下代码输出什么?
0.1 + 0.2 === 0.3 //false //结果为false,这是由浮点数内部表示导致的。0.1+0.2并不刚好等于0.3,实际结果为 0.30000000000000004
11、如何向Array 对象添加自定义方法,让下面的代码可以运行?
var arr = [1, 2, 3, 4, 5]; var avg = arr.average(); console.log(avg);
思路: 用原型 prototype
,由于JS的每个对象都链接到另一个对象(也就是对象的原型),并继承原型对象的方法,你可以跟踪每个对象的原型链,
直到到达没有原型的null对象,我们需要通过修改Array原型来向全局Array对象添加方法。
Array.prototype.average = function () { //计算sum 的值 var sum = this.reduce(function (prev, cur) { return prev + cur; }); //将sum除以元素个数并返回 return sum / this.length; } var arr = [1, 2, 3, 4, 5]; var avg = arr.average(); console.log(avg);//3
12、什么是window对象?什么是document对象?
window 对象代表浏览器中打开的一个窗口。 document对象代表整个HTML文档。实际上,document对象是window对象的一个属性。
13、AMD(Modules/Asynchronous-Definition) 和 CMD (Common Module Definition)规范区别?
说明: AMD
是 RequireJS
在推广过程中对模块定义的规范化产出。CMD
是 SeaJS
在推广过程中对模块定义的规范化产出。
区别:
对于依赖的模块,AMD 是提前执行,CMD是延迟执行。不过RequireJS
从 2.0
开始,也改成延迟执行。
CMD 推崇依赖就近,AMD 推崇依赖强制。
AMD 的 API 默认是一个当多个用,CMD 的API 严格区分,推崇职责单一。
// CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // 此处略去 100 行 var b = require('./b') // 依赖可以就近书写 b.doSomething() }) // AMD 默认推荐 define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好 a.doSomething(); // 此处略去 100 行 b.doSomething(); })
14、JS延迟加载方式有哪些?
defer 和 async 动态创建DOM方式(用的最多),按需异步载入JS。 defer:延迟脚本。立即下载,但延迟执行(延迟到整个页面都接卸完毕后再运行),按照脚本的先后顺序执行。 async:异步脚本。下载完立即执行,但不能保证按脚本出现的先后顺序执行。
15、下面代码输出什么?
var y = 1; if (function f(){}) { y += typeof f; } console.log(y); // 1undefined
16、写一个 mul 函数,使用方法如下:
console.log(mul(2)(3)(4)); // output : 24 console.log(mul(4)(3)(4)); // output : 48
答案:
//使用嵌套闭包即可 function mul(x) { return function (y) { return function (x) { return x * y * z; } } }
17、JavaScript 怎么清空数组
var arrayList = ['a','b','c','d','e','f']; //方法1: arrayList = []; //方法2: arrayList.length = 0; //方法3: arrayList.splice(0, arrayList.length);
18、怎么判断一个object是否是数组(array)?
方法一:使用 Object.prototype.toString 来判断
function isArray(obj){ return Object.prototype.toString.call( obj ) === '[object Array]'; }
方法二:使用原型链
function isArray(obj){ return obj.__proto__ === Array.prototype; }
方法三:利用JQuery
function isArray(obj){ return $.isArray(obj) }
19、下面代码输出什么?
var output = (function (x) { delete x; return x; })(0) console.log(output);//输出是 0。 delete 操作符是将object的属性删去的操作。但是这里的 x 是并不是对象的属性, delete 操作符并不能作用。
20、下面代码输出什么?
var Employee = { company: 'xyz' } var emp1 = Object.create(Employee); delete emp1.company; console.log(emp1.company); // xyz //原因是 这里的 emp1 通过 prototype 继承了 Employee 的company。emp1 并没有自己的 company属性,此处delete 无效。
21、什么是undefined x 1?
说明:当我们使用delete 删除一个数组中的元素,这个元素的位置就会变成一个占位符,打印出来就是 undefined x 1
var trees = ["redwood","bay","cedar","oak","maple"]; delete trees[3]; console.log(trees); // ["redwood", "bay", "cedar", empty, "maple"] console.log(trees[3]); //undefined
22、下面代码输出什么?
var trees = ["xyz","xxxx","test","ryan","apple"]; delete trees[3]; console.log(trees.length);//5 delete 操作符删完数组元素后,并不影响数组长度
23、下面代码输出什么?
var bar = true; console.log(bar + 0);//1 console.log(bar + "xyz");//truexyz console.log(bar + true); //2 console.log(bar + false); //1
可以对照如下操作:
Number + Number -> 加法 Boolean + Number -> 加法 Boolean + Boolean -> 加法 Number + String -> 连接 String + Boolean -> 连接 String + String -> 连接
24、下面代码输出什么?
var z = 1, y = z = typeof y; console.log(y);
上面代码等价于
var z = 1 z = typeof y; var y = z; console.log(y);// undefined
25、下面代码输出什么?
var foo = function bar(){ return 12; }; typeof bar(); //抛异常, Uncaught ReferenceError: bar is not defined
如下修改不会抛异常
var bar = function(){ return 12; }; typeof bar(); //或者 function bar(){ return 12; }; typeof bar();
26、如下两种函数声明有什么区别?
//foo的定义是在运行时,这种声明方式会有变量提升问题 var foo = function(){} function bar(){}
如下:使用匿名方式声明,定义的变量会导致变量提升,所以上述代码执行顺序如下
// foo bar的定义位置被提升 function bar(){}; var foo; console.log(foo)// undefined foo = function(){};
27、如下代码输出什么?
var salary = "1000$"; (function () { console.log("Original salary was " + salary); var salary = "5000$"; console.log("My New Salary " + salary); })(); // 答案: //Original salary was undefined // My New Salary 5000$
考察变量提升,上述函数中的 console.log("Original salary was " + salary);
这行代码,先使用了 salary 这个变量,函数内部会有变量提升问题,等价于如下:
var salary ; console.log("Original salary was " + salary);
28、如果我使用JS的“关联数组”,我们怎么计算“关联数组”的长度?
var counterArray = { A : 3, B : 4 }; counterArray["C"] = 1; //答案:直接计算key 的数量就可以了 Object.keys(counterArray).length // Output 3
29、JavaScript 中有哪些弹出框?
Alert、Confirm 、Prompt
30、Void(0)
怎么用?
Void(0)用于防止页面刷新,并在调用时传递参数“zero”。 Void(0)用于调用另一种方法而不刷新页面。
31、如何创建通用对象?
var I = new object();