js基础(1): this, 闭包, 作用域

62 阅读5分钟

原型链

function的原型是prototype

实例对象的原型是__proto__

person.constructor === Person.prototype.constructor
//因为对象本身没有,所以会去原型链上去寻找
//每一个对象都会从原型上继承(委托)属性

访问对象属性时,就近查找, 找不到去链上寻找.

作用域

定义变量的区域, 支持定义变量的存址与操作

var value = 1

function foo() {
	console.log(value)
}

function bar() {
	let value = 2
	foo()
}

静态作用域(词法作用域)

函数的作用域在函数定义的时候决定,

在调用foo时,现在内部AO中寻找, 找不到去沿着作用域链寻找

foo:AO
{
	arguments: {
		0: 0,
		length: 0
	}
}

foo.[[scope]] = [
	globalContext.VO
]

这些是在定义时确定好的, 因此找value不回去bar中,而是去全局对象中

因此上述代码会输出1

动态作用域

在函数执行调用时决定

上述代码会输出2

执行上下文

可执行代码: executable code: 全局代码, 函数代码, eval代码

js中会创建一个上下文的栈 , execution context stack ECS

作用域与执行上下文没有关系

执行上下文的属性

  • 变量对象
  • 作用域链
  • this

变量对象 VO variable object

全局环境中定义的对象为全局对象

函数:函数中的对象叫 AO(activation object), 需要进入与离开上下文, 因此为活动对象

argumnets: 类数组, 创建一个函数时, 这是默认加入的值

AO

  • 函数的形参, 没有的话默认undefined
  • 声明的变量
  • 当前代码段中默认的属性
function foo(a) {
  var b = 2;
  function c() {}
  var d = function() {};
  b = 3;

}

foo(1)
//初始
AO = {
    //固有的
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,
    b: undefined,
    //注意此处, 此处是导致同名function会覆盖的原因, 因为进入之后,会依据代码更新AO, 因此后面的函数会覆盖掉前面的同名函数
    c: reference to function c(){},
    d: undefined
}

作用域链

有一个ECStack , 栈

在当前AO找不到属性, 则会想VO中去找, 知道找到全局的VO

在执行代码过程中,与原型链不同

function foo() {
    function bar() {
        ...
    }
}

//注意递归可能会出现爆栈的问题, 因为栈的容量有限
foo.[[scope]] = [
  globalContext.VO
];

bar.[[scope]] = [
    fooContext.AO,
    globalContext.VO
];   

闭包

MDN:闭包是能够访问自由变量的函数

自由变量: 自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。

因此闭包 = 函数 + 函数能够访问的自由变量

《JavaScript权威指南》中就讲到:从技术的角度讲,所有的JavaScript函数都是闭包。

ECMAScript中,闭包指的是:

  1. 从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域;
  2. 从实践角度:以下函数才算是闭包:
    1. 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回);
    2. 在代码中引用了自由变量;

this指向

ECMAScript 的类型分为语言类型和规范类型。

ECMAScript 语言类型是开发者直接使用 ECMAScript 可以操作的。其实就是我们常说的Undefined, Null, Boolean, String, Number, 和 Object。

而规范类型相当于 meta-values,是用来用算法描述 ECMAScript 语言结构和 ECMAScript 语言类型的。规范类型包括:Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, 和 Environment Record。

Reference

Reference 是一个 Specification Type,也就是 “只存在于规范里的抽象类型”。它们是为了更好地描述语言的底层行为逻辑才存在的,但并不存在于实际的 js 代码中。

Reference 的构成,由三个组成部分,分别是:

  • base value;
  • referenced name;
  • strict reference;

我们简单的理解的话:

base value 就是属性所在的对象或者就是 EnvironmentRecord,它的值只可能是 undefined, an Object, a Boolean, a String, a Number, or an environment record 其中的一种。

referenced name 就是属性的名称

strict reference就是是否为严格模式

var foo = 1;

// 对应的Reference是:
var fooReference = {
    base: EnvironmentRecord,
    name: 'foo',
    strict: false
};
var foo = {
    bar: function () {
        return this;
    }
};
 
foo.bar(); // foo

// bar对应的Reference是:
var BarReference = {
    base: foo,
    propertyName: 'bar',
    strict: false
};

GetBase()

返回 reference 的 base value。

IsPropertyReference

如果 base value 是一个对象,就返回true。

如何判断this指向

  1. 计算 MemberExpression 的结果赋值给 ref;
  2. 判断 ref 是不是一个 Reference 类型;
    1. 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)
    2. 如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么this的值为 ImplicitThisValue(ref)
    3. 如果 ref 不是 Reference,那么 this 的值为 undefined;

1:找MemberExpression

function foo() {
    console.log(this)
}

foo(); // MemberExpression 是 foo

function foo() {
    return function() {
        console.log(this)
    }
}

foo()(); // MemberExpression 是 foo()

var foo = {
    bar: function () {
        return this;
    }
}

foo.bar(); // MemberExpression 是 foo.bar

简单讲, memberExpression是()左边的东西

2:判断reference

var value = 1;

var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}

//示例1
//此时MemberExpression为foo.bar, foo.bar的 var Reference = {   base: foo,   name: 'bar',   strict: false };
//根据上面的流程, 此时的this指向base即foo
console.log(foo.bar()); // 2
//示例2
//与示例一相同
console.log((foo.bar)()); // 2
//示例3
//有赋值操作符,使用了GetValue, 所以返回的不是Reference
//()左侧是一个运算 ,因此this指向undefined, 非严格模式undefined转为全局, 因此为1
console.log((foo.bar = foo.bar)()); // 1
//示例4
//与3类似
console.log((false || foo.bar)()); // 1
//示例5
//使用了GetValue
console.log((foo.bar, foo.bar)()); // 1

简单来说:始终指向调用它的对象

重点搞懂!!!静态作用域, 执行上下文,闭包, chain, this指向