闭包,原型链
闭包
在【函数定义】的时候,【函数对象】的【scope属性】便会引用包含该函数的上一级函数在运行时所生成的【执行上下文】的【活动对象】,因为发生对象属性之间的相互依赖,导致包含此函数的【scope属性】所指向的活动变量,在上一级函数【执行完毕】将要【释放执行上下文】的时候【不能释放】其包含的【活动变量】。
自己的理解: 闭包的产生首先要有一个地址被引用所指向
函数与对象的关系
- 函数就是对象的一种
- 对象是函数创建的
prototype原型
- prototype是函数的一个属性
- 每一个构造函数都有prototype属性
- prototype的值实际上是一个对象
- 这个对象默认只有一个constructor的属性,并且指向函数本身
构造函数中的属性和原型中的属性的区别
- 把属性定义在原型中比定义在构造函数中消耗的内存更小,因为在内存中一个类的原型只有一个,写在原型中的行为可以 被所有实例共享。实例化的时候并不会在内存中再复制一份。没有特殊原因,一般把属性写到类中,行为写到原型中。
- 使用原型的方式定义属性,实际上不同对象中的属性是共享的,也就是说对其中的任何一个对象修改了属性,其他对象的属性也会跟着发生变化,因为它们指向的是同一个地址,共享同一个属性
- 构造函数中定义的属性和方法比原型中定义的属性和方法优先级高。如果定义了同名称的属性和方法,构造函数中会覆盖原型中的。
隐式原型(对象)
隐式原型:_proto_是对象中的一个属性,每个对象都有一个_proto_属性,也称为隐式原型。并且指向创建该对象的prototype
- 自定义函数本质上都是通过Object函数来创建的,所以它的_proto_指向Object.prototype
- Object.prototype的_proto_指向null
原型链
原型链:若访问对象属性时,先在基本属性中找,如果自身没有该属性,便会沿着_proto_这条链往上找。
执行上下文
在一段js代码执行前,浏览器已经做了一些准备工作,其中包括对变量的声明,变量赋值是在赋值语句执行的时候进行的。
准备工作有三步:
- 变量:变量的声明,默认赋值为undefined
- this:赋值
- 函数声明:赋值
这三种数据的准备情况称之为“执行上下文”或者“执行上下文环境”;
函数每被调用一次,都会产生一个新的执行上下文环境,因为不同的调用可能会有不同的参数。
函数定义的时候就应经确定了函数体内部变量的作用域
执行上下文栈
执行全局代码时,会产生一个全局上下文环境,每次调用函数,又会产生函数上下文环境,当函数调用完成时,这个上下文环境及其中的数据都会被销毁。再重新回到全局上下文环境。
处于活动状态的执行上下文环境只有一个。(压栈和出栈的过程)
作用域和执行上下文
除全局作用域之外,每个函数都会创建自己作用域。作用域在函数定义时就已经确定了,而不是在调用时确定。 作用域只是一个“地盘”,一个抽象的概念,其中没有变量,要通过作用域对应的执行上下文来获取变量的值,同一作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的值。
自由变量
在一个作用域中使用的变量,没有在其中声明(即在其他作用域中声明的),此时,这个变量对这个作用域来讲就是自由变量(也可称为活动变量)
作用域链
- 先在当前作用域查找X,如果有则获取并结束,如果没有则继续;
- 如果当前作用域是全局作用域,则证明X未定义,结束;否则继续;
- 将创建该函数的作用域作为当前作用域;
- 跳转到第一步。
继承
-
对象冒充
function Person(name, age) { this.name = name; this.age = age; this.sayName = function() { console.log(this.name); }; } function Student(name, age) { this.obj = Person; this.obj(name, age); delete this.obj; this.study = function() { console.log("study"); } } var stu1 = new Student("zhangsan", 21); console.log(stu1.name, stu1.age); stu1.sayName(); stu1.study();
-
原型链的方式
function Person(name, age) { this.name = name; this.age = age; this.sayName = function () { console.log(this.name); }; } function Student(name, age) { this.constructor(name, age); } Student.prototype = new Person(); var stu1 = new Student("zhangsan", 21); console.log(stu1.name, stu1.age);
-
call和apply方法
function Student(name, age) { // Person.call(this, name, age); Person.apply(this, [name, age]); } var stu = new Student("zhangsan", 21); console.log(stu.name, stu.age);
-
混合方式
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayName = function() { console.log(this.name); }; function Student(name, age) { Person.call(this, name, age); } Student.prototype = new Person();