四、原型与原型链
1. 原型
(一)原型的定义
在JavaScript中是使用构造函数(js是基于原型的语言)来新建一个对象的,每一个构造函数的内部都有一个 prototype 属性,它的属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。
当使用构造函数新建一个对象后,在这个实例对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。
一般来说不应该能够获取到原型的,但是现在浏览器中都实现了 proto 属性来访问原型,但是最好不要使用这个属性,因为它不是规范中规定的。ES5 中新增了一个 Object.getPrototypeOf() 方法,可以通过这个方法来获取对象的原型。
一个实例的__proto__ // 隐式原型
一个实例的构造函数的prototype // 显式原型
(二)原型的特性
- 所有的引用类型(数组、对象、函数)都具有对象特性,即可自由扩展属性( null 除外); 都有一个 proto 属性,属性值是一个普通的对象, proto 属性值指向它的构造函数的 prototype 属性值(实例. proto === 构造函数.prototype)
- 所有的函数都有一个 prototype 属性,属性值也是一个普通的对象(这里要注意,一般实例的构造函数上才有prototype 属性,但函数本身就有prototype 属性)
(三)hasOwnProperty
使用 hasOwnProperty 判断这个属性是不是对象本身的属性:
高级浏览器已经在 for in 中屏蔽了来自原型的属性,但有时候加上原型的判断,可以保证程序的健壮性:
f.hasOwnProperty(item) => 判断item是否是f对象本身的属性
2. 原型链
(一)原型链的定义/什么是原型链
每个构造函数内部都有显式原型prototype,构造函数的实例对象具有隐式原型proto属性指向它构造函数的显示原型prototype,如果一个构造函数是另一个构造函数的的实例对象的话,那这个构造函数也有proto。当我们查找一个对象的属性时,会先从自身属性上去找,找不到的话再从proto上去找,还没有就从proto的proto上去找,这样一直往上查就会查到原型的尽头,这么一个链式结构是原型链,原型链尽头是null。
(二)原型链终端
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的 proto (即它的构造函数的 prototype )中寻找 ;顺着原型链找,最后没找到的话最终遇到null,null是一个中断机制,遇到null就停了返回undefined。Object.prototype. proto === null
为什么Object.prototype.proto指向null?
因为原型链的终端是null,就和链表一样,终端是null,而原型链的终端是Object这个构造函数的原型的proto指向的值,设计就是这样
(1)原型链上查找一个不存在的属性为什么返回undefined
(2)Object.prototype.proto为什么设计指向null ?
查找属性是要沿着原型链递归查找,所以原型链是要有长度的,null作为一个为空的对象刚好满足要求,是个相对合理的设定;原型链查找就是递归调用,那肯定要有出口的,递归出口就是当前原型对象的原型是不是null
(3) 原型链的终点是什么?如何打印出原型链的终点?
由于Object是构造函数,原型链终点是Object.prototype.proto,
而Object.prototype.proto=== null // true,
所以,原型链的终点是null。原型链上的所有原型都是对象,所有的对象最终都是由Object构造的,而Object.prototype的下一级是Object.prototype.proto。
(三)新建的对象为什么能够使用 toString() 等方法?
新建对象原型链上有Object,就会继承 Object.prototype属性 (Object.prototypeproto === null),所有的对象都继承了Object的方法,所以这就是新建的对象为什么能够使用 toString() 等方法的原因。
(四)原型链的特点
JavaScript 对象是通过引用来传递的,创建的每个新对象实体中并没有一份属于自己的原型副本。当修改原型时,与之相关的对象也会继承这一改变。
(五)原型链图
(六)Function.proto === Function.prototype
为什么 Function.prototype === Function.proto 为true?
因为Function.prototype.proto指向的是Object.prototype,Function.proto.proto === Object.prototype所以Function.prototype === Function.proto;所以Object.proto === Function.prototype
https://github.com/mqyqingfeng/Blog/issues/2
3. 原型修改、重写
function Person(name) {
this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true
// 重写原型
Person.prototype = {
getName: function() {}
}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // false
可以看到修改原型的时候p的构造函数不是指向Person了,因为直接给Person的原型对象直接用对象赋值时,它的构造函数指向的了根构造函数Object,所以这时候p.constructor === Object ,而不是p.constructor === Person。要想成立,就要用constructor指回来:
Person.prototype = {
getName: function() {}
}
var p = new Person('hello')
p.constructor = Person
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true
4. 原型链指向
p.__proto__ // Person.prototype
Person.prototype.__proto__ // Object.prototype
p.__proto__.__proto__ //Object.prototype
p.__proto__.constructor.prototype.__proto__ // Object.prototype
Person.prototype.constructor.prototype.__proto__ // Object.prototype
p1.__proto__.constructor // Person
Person.prototype.constructor // Person
p.__proto__ // Person.prototype
Person.prototype.__proto__ // Object.prototype
p.__proto__.__proto__ //Object.prototype
p.__proto__.constructor.prototype.__proto__ // Object.prototype
Person.prototype.constructor.prototype.__proto__ // Object.prototype
p1.__proto__.constructor // Person
Person.prototype.constructor // Person

Object.prototype.__proto__ 原型链的尽头是null
Function.prototype.__proto__ Object.prototype,Function是Object实例化的对象
构造函数自身的__proto__是什么 任何函数包括Function的隐式原型都指向Function.prototype;
Object.__proto__ 答案是什么 可通过new Object的方式去new实例,说明Object是构造函数,任何函数的隐式原型是Function.prototype;
Object instanceof Function true Object的原型链上有Function
Function instanceof Object true Function的原型链上有Object
Function.prototype === Function.__proto__ true
通常,proto指向prototype;constructor指向构造函数;constructor和prototype是相反方向的箭头
7. 如何获得对象非原型链上的属性?
使用后hasOwnProperty()方法来判断属性是否属于原型链的属性:
function iterate(obj){
var res=[];
for(var key in obj){
if(obj.hasOwnProperty(key))
res.push(key+': '+obj[key]);
}
return res;
}
五、执行上下文/作用域链/闭包
1. 对执行上下文的理解
(一)执行上下文的定义
在一段 JS 脚本(即一个



