原型、原型链、作用域、作用域链、闭包

612 阅读4分钟

原型和原型链

原型

概念

构造函数、原型、实例的关系:每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。

①所有引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
②所有函数都有一个prototype(原型)属性,属性值是一个普通的对象
③所有引用类型的__proto__属性指向它构造函数的prototype

原型链

概念

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。

默认原型

默认情况下,所有的引用类型都继承自Object,即Object.prototype 位于原型继承链的顶端。

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
};
function SubType(){
    this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
     return this.subproperty;
}
let instance = new SubType();
console.log(instance.getSuperValue()); // true

此时如果调用instance.toString()方法,则实际上的调用的Object.prototype上的方法。

原型与继承关系

instanceof

使用instanceof操作符可以确定原型与实例之间的关系。

console.log(instance instanceof Object); // true
console.log(instance instanceof SuperType); // true
console.log(instance instanceof SubType); // true

isPrototypeof()

使用isPrototypeof()方法,只要原型链中包含此原型,就会返回true。

console.log(Object.prototype.isPrototypeOf(instance)); // true
console.log(SuperType.prototype.isPrototypeOf(instance)); // true
console.log(SubType.prototype.isPrototypeOf(instance)); // true

作用域和作用域链

作用域

作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限。

作用域分为:全局作用域、函数作用域

由于作用域的限制,每段独立的执行代码块只能访问自己作用域和外层作用域中的变量,无法访问到内层作用域的变量。

词法作用域

JavaScript采用的是词法作用域,即函数的作用域在函数定义的时候就决定了。

与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定。

var value = 1;
 
function foo() {
    console.log(value);
}
 
function bar() {
    var value = 2;
    foo();
}
 
bar(); // 1

作用域链

当可执行代码内部访问变量时,会先查找本地作用域,如果找到目标变量即返回,否则会去父级作用域继续查找...一直找到全局作用域。我们把这种作用域的嵌套机制,称为 作用域链

function foo(a) {
  var b = a * 2;

  function bar(c) {
    console.log( a, b, c );
  }

  bar(b * 3);
}

foo(2); // 2 4 12

一共有三层作用域嵌套:

  1. 全局作用域
  2. 函数foo作用域
  1. 函数bar作用域

其中,函数参数包含在函数作用域中。

块级作用域

if (true) {
  var a = 1;
}

console.log(a); // 1

运行上面的例子可以知道JavaScript原生不支持块级作用域,

在es6中提出了let和const关键字,使用let和const可以创建块级的作用域。

let和const区别:let声明的变量可以改变,值和类型都可以改变;而const声明的常量不可以改变,这意味着const一旦声明,就必须立即初始化,不能以后再赋值。

闭包

参考

什么是闭包

闭包就是指有权访问另一个函数作用域中的变量的函数

把函数执行,形成私有上下文,并且保存和保护私有变量的机制,称为“闭包”,它是一种机制。

简单来讲,闭包就是函数内部定义的函数,被返回了出去并在外部调用。

function foo() {
  var a = 2;

  function bar() {
    console.log( a );
  }

  return bar;
}

var baz = foo();

baz(); // 这就形成了一个闭包

闭包的作用

  1. 保护:划分一个独立的代码执行区域,在这个区域中有自己私有变量存储的空间,而用到的私有变量和其它区域中的变量不会有任何的冲突(防止全局变量污染)
  2. 保存:如果上下文不被销毁,那么存储的私有变量的值也不会被销毁,可以被其下级上下文中调取使用