作用域,闭包

es6 之前,js 只有全局作用域和函数作用域

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
console.log(foo);
function func() {
  var bar = 1;
  return function() {
    console.log(bar++);
  };
}
var foo = 123;
var func2 = func();
func2(); //1
func2(); //2

这段代码有两个阶段,创建和执行。创建即创建执行上下文,包括变量对象,this,arguments。执行即 js 执行

执行和创建不是完全分开的,执行的时候也会创建。

  1. 创建阶段(变量提升,函数提升)
  1. 执行阶段

(1) 执行

1
2
console.log(foo); //undefined
func(); //创建函数执行上下文

(2) 又创建了

可以看出是一个栈的结构,因此也可以执行上下文栈

(3) 继续执行

1
bar = 1;

这时返回一个函数,形成了闭包。(闭包可以理解为,把上一层的执行执行上下文保存一下,不会在函数执行结束后就销毁)

(4) 继续执行

1
func2(); //1,拿到的是闭包中的bar

作用域链,函数作用域查找一个变量没有定义,会向上查找,一直到全局作用域都没找到就会报错 x is not defined

原型和原型链,继承

定义一个构造器

1
2
3
4
function Person(name) {
  // 私有
  this.name = name;
}

创建实例

1
2
var p1 = new Person("zhangsan");
console.log(p1.name); //zhangsan

原型或原型对象(可以理解为就是构造器的一个属性,指向一个对象)

1
2
3
Person.prototype.say = function() {
  console.log(`my name is ${this.name}`);
};

原型上的方法可以理解为公共方法(可以节省空间,不用每个实例都创建一个 say 方法)。

1
p1.say();

当访问 say 方法时,先在实例上找,没找到就去原型对象上找,还没找到,就去原型对象的原型对象上找…这就是原型链

等等,原型对象的原型对象是什么?这就说到继承了

声明一个 Worker 构造器

1
2
3
function Worker(job) {
  this.job = job;
}

worker 也是 person,所以要继承 Person 类

1.原型链继承(基础版)

1
2
3
4
5
Worker.prototype = new Person();
Worker.prototype.constructor = Worker;
var w1 = new Worker();
w1.name = "lisi";
w1.say();

缺点:

2.借用构造函数

构造函数修改 this 指向

1
2
3
4
function Worker(name, job) {
  Person.call(this, name);
  this.job = job;
}

缺点:没有继承父类原型上的方法

3.组合继承

1
2
3
4
5
function Worker(name, job) {
  Person.call(this, name);
  this.job = job;
}
Worker.prototype = Person.prototype;

缺点:父类和子类的原型指向了同一个原型对象

4.寄生组合继承

使用 Object.create(),创建一个对象,它的原型是 Person.prototype

1
2
3
4
5
function Worker(name, job) {
  Person.call(this, name);
  this.job = job;
}
Worker.prototype = Object.create(Person.prototype);