本来是只想作为自己的学习日记来的,可是掘金貌似不支持紧自己可见
1、js是静态作用域,函数的作用域链是在函数创建的时候就确定了;
2、遇到函数执行的时候,才会创建执行上下文;
3、每个执行上下文都有三个重要属性:
-
作用域链; -
变量对象; -
this
4、变量对象初始化只有arguments属性,然后进入执行上下文,开始初始化变量对象,初始值为undefined。执行代码,再开始复赋值;
5、函数创建的时候,会使用[[scope]]属性,保存父变量对象。开始准备工作,首先创建自己的作用域链,然后初始化变量对象并赋值。最后将变量对象放入作用域链顶端,开始执行代码
6、this在大部分情况是指向调用它的对象,但是根据规范来的话,如果在调用前的部分也就是()左边发生了运算符运算或者赋值操作,this会指向undefined,非严格模式下,this就是全局对象window
eg:
var value = 1;
var foo = {
value: 2,
bar: function () {
return this.value;
}
}
//示例1
console.log((foo.bar = foo.bar)()); // 1
//示例2
console.log((false || foo.bar)()); // 1
7、同一个函数形成的多个闭包的值是相互独立的,可以理解为函数创建的时候,保存父变量对象是进行了一份深拷贝
8、为什么闭包中父函数执行上下文已经销毁了,但是闭包return的函数还是能读取到父函数中的变量呢?这是因为闭包return的函数创建时,已经保存了父函数的变量对象形成了自己的作用域链,所以闭包return的函数可以从自己的作用域链中读取父函数的变量对象。
9、函数参数的传递,分为按值传递,引用传递和共享传递,按值传递就是传递的参数是基本数据类型会拷贝一份,即便在函数里修改行参,也不会影响实参。但是传入的引用数据类型进行引用数据的值的修改修改就会影响实参。但是函数参数传递是使用的共享传递,会将传入的参数拷贝一份再传入函数。看个例子:
var obj = {
value: 1
};
// 这个就是引用传递,其实就是行参o和实参obj都指向同一个引用地址
function foo(o) {
o.value = 2;
console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
var obj = {
value: 1
};
// 这个就是共享传递,其实就是行参是重新声明了一个变量,然后改了该行参的引用,所以不会影响到实参
function foo(o) {
o = 2; // 这里改了引用
console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1
10、call和apply的实现,大致原理就是在被绑定的对象上把调用call或者apply的函数添加为属性。这样就会就能改变this指向,然后再删除添加函数的属性。代码如下:
// 第三版
Function.prototype.call2 = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
// 测试一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call2(null); // 2
console.log(bar.call2(obj, 'kevin', 18));
// 1
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}