原型
- 每个对象都有
__proto__
属性,并且指向它们的原型对象。 - 每个构造函数都有它的prototype原型对象。
- prototype原型对象里面的constructor指向它的构造函数。
- new一个构造函数会形成它的实例对象。
原型链
- 当我们查找特定属性的时候,我们先去这个对象里面找,如果没有的话就去它的
原型对象
里面找,如果还没有的话就去原型对象
的原型对象
找,这个操作就是被委托在整个原型链
上。
举个例子 -
为什么有 valueOf / toString 属性呢?
var obj = { name:"obj"} console.dir(obj)
我们发现 console.dir(obj) 打出来的结果是:
-
obj 本身有一个属性 name(这是我们给它加的)
-
obj 还有一个属性叫做
__proto__
(它是一个对象) -
obj.__proto__
有很多属性,包括 valueOf、toString、constructor 等 -
obj.__proto__
其实也有一个叫做__proto__
的属性(console.log 没有显示),值为 null
现在回到我们的问题:obj 为什么会拥有 valueOf / toString / constructor 这几个属性?
答案:
这跟 __proto__
有关。
当我们「读取」 obj.toString 时,JS 引擎会做下面的事情:
-
看看 obj 对象本身有没有 toString 属性。没有就走到下一步。
-
看看
obj.__proto__
对象有没有 toString 属性,发现obj.__proto__
有 toString 属性,于是找到了
所以 obj.toString 实际上就是第 2 步中找到的obj.__proto__.toString
。
可以想象,
-
如果
obj.__proto__
没有,那么浏览器会继续查看obj.__proto__.__proto__
-
如果
obj.__proto__.__proto__
也没有,那么浏览器会继续查看obj.__proto__.__proto__.proto__
-
直到找到 toString 或者
__proto__
为 null。
上面的过程,就是「读」属性的「搜索过程」。
而这个「搜索过程」,是连着由 __proto__
组成的链子一直走的。
这个链子,就叫做「原型链」。
闭包
- 闭包就是能够读取其他函数内部变量的函数。
- 闭包是 JS 函数作用域的副产品。
- 换句话说,正是由于 JS 的函数内部可以使用函数外部的变量,所以这段代码正好符合了闭包的定义。而不是 JS 故意要使用闭包。
- 暴露一个访问器(函数),让别人可以「间接访问」。而不是直接暴露在全局上。
var local = '变量';
function foo(){
console.log(local)
}