JavaScript 是一门基于原型的语言,它没有建立类,对象被建立是基于引用的,JavaScript 也是一种动态编程语言,这意味着能够在实例化后轻松地在对象中添加或删除属性。html
在 Js 中没有类的概念,在 Js 中所存在的是构造函数代替模拟类的存在,对象由某个构造函数构造出来,咱们称之为这个对象是 构造函数的实例, 即便是使用 var 声明的一个对象,咱们也能够看到他的
__proto__
中的 constructor 也是指向的 Object 。git
__proto__
咱们已知函数经过 new 关键字调用就是所谓的构造函数,获得一个实例。在这个构造函数,或者说 每一个函数 上面,都会有一个 prototype 属性,这个 prototype 属性 指向了一个对象,暂且无论这个对象是什么,只须要知道 函数 有一个 prototype 属性,这个属性能够经过 点的方式访问到。github
在构造函数所构造的实例上,或者说 Js 中的每个对象上都会有一个
__proto__
属性(这个属性虽然能够看到,也能够访问到,但并不建议直接使用 修改 对象的原型;详见)编程实例的
__proto__
也指向了一个对象,实例的__proto__
和构造函数的 prototype 指向的是同一个对象,咱们称这个对象为 原型对象segmentfault
function Fn() {
this.name = 'Zyc'
}
var f = new Fn()
console.log(Fn.prototype) // 一个 Object
console.log(f.__proto__) // 一个 Object
console.log('Fn & f', Fn.prototype === f.__proto__) // true
复制代码
从位置上来讲,确实如上所述,那么 究竟什么是所谓的 原型 呢,咱们须要再从 构造函数出发:bash
函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而建立的实例的原型,也就是 上述 实例 f 的原型。 而所谓的原型就是:每个 JavaScript 对象(null 除外) 在建立的时候就会与之关联另外一个对象,这个对象就是咱们所说的原型,每个对象都会从 原型 "继承" 属性编程语言
在每一个 原型对象 中,都会有一个 constructor 属性,这个属性指向其关联的构造函数函数
function Person() {
}
var person1 = new Person()
console.log('Person && person1', Person.prototype === person1.__proto__) // true
console.log(Person.prototype.constructor === Person) // true
复制代码
其实在构造函数内部直接定义方法也是能够的,问题在于,每一个构造函数均可能会调用屡次去构建实例,若是把方法定义在 构造函数内部,那么在每次调用的时候,都会从新建立这个函数,伴随着函数的建立,会在内存中开辟一块空间存储这个函数,可是每一个实例用到的方法内部代码彻底一致,就形成了资源浪费。性能
若是是将方法定义在函数的 原型 上面,全部的实例都可以访问这同一个方法,不会形成资源浪费,代码更高效学习
还有一种解决方案,就是在构造函数外部预先定义好一个函数,在构造函数内部引用这个函数,那么全部的实例将拥有同一个函数的地址,也能够避免资源浪费
Ps:一个核心观念就是,构造函数会被调用屡次建立实例,每一次调用就会伴随着 函数内部代码的执行,若是内部有函数的声明,将会不断的开辟内存空间,就会资源浪费,若是有方法实现公用一个内存地址,那么就能够避免掉性能浪费的问题
当咱们在获取一个对象的属性时,会优先在对象内部查找这个属性,当在对象内部没有找到这个属性时,就会去对象的原型上面去找,若是在实例的原型上面也没有找到,就会去原型的原型上面去找。
function Person() {
}
Person.prototype['hobby'] = ['唱', '跳', 'Rap']
var person1 = new Person() // 此时 Person 的实例 person1 中并无 hobby 这个属性
console.log(person1.hobby); // ['唱', '跳', 'Rap']
person1['hobby'] = ['看书', '学习', '运动'] // 给 person1 实例添加了 hobby 属性
console.log(person1.hobby); // ['唱', '跳', 'Rap']
复制代码
已知 原型对象 也是一个对象,其实原型对象就是经过 Object 构造函数生成的,也就是说,一个实例的原型对象 是 Object 的实例, 那么继而能够得出 原型对象 的
__proto__
和 Object 构造函数 的 prototype 指向同一个地址,这个地址也就是 Object 的 原型
Object.prototype['address'] = 'NJ'
function Person() {
}
Person.prototype['hobby'] = ['唱', '跳', 'Rap']
var person1 = new Person()
console.log(person1.hobby); // ['唱', '跳', 'Rap']
person1['hobby'] = ['看书', '学习', '运动']
console.log('hobby:', person1.hobby); // ['唱', '跳', 'Rap']
// 在 Person 的 原型 -> 原型 上定义的 address
console.log('address:', person1.address); // NJ
// 未在任何地方定义的属性:
console.log('age:', person1.age); // undefined
复制代码
引上述:对象属性的查找规则,咱们 既没有在 Person 的 prototype 上定义 address 也没有在 实例内部定义 address,而是在 Object 的 prototype 上定义的 address,在打印
person1.address
时的确打印了出来数据。若是未在任何地方定义的数据 最终获取 为 undefined证:对象查找属性规则 = 对象内部 > 原型 > 原型 > 原型 > undefined
截止到上面,应该能够体会到一个如同链条的存在,所谓的原型链,就是 原型 => 原型 => 原型 原型串联起来的链条,而在一个对象查找一个属性时,就会在原型链上逐步往上查找,要么找到,要么没找到也就是 undefined
Ps:
function Person() {
}
var person2 = new Person()
console.log(person2 instanceof Person); // true
console.log(person2 instanceof Object); // true
复制代码
如上所述:JavaScript 判断一个对象是否 instanceof 某个函数的依据,即对象 person2 的 原型链 上有没有一个
__proto__
是这个函数的 prototype, 若是有,那么 person 就是这个函数的 instance。因为通常全部的原型链最终都会指向顶端的 Object.prototype,因此它们都是 Object 的 instance。一句话理解 instanceof 的运算规则为: instanceof 检测左侧的
__proto__
原型链上,是否存在右侧的 prototype 原型。
首先 原型链 必须也然有终点,若是没有终点在查找对象的属性时会无限查找下去 其次 原型链 的终点并非 Object.prototype; 亦能够换种说法,原型链的终点就是 Object.prototype 而这个 Object.prototype 是一个特殊的 Object 它特殊在 它的
__proto__
指向 null。而我我的更偏向于考虑原型链 的终点为 null。首先须要明确一点,原型链是指 对象 的原型链,因此原型链上的全部节点都是 对象, 不能是字符串、数字、布尔等原始类型。 另外规范要求原型链必须是 有限长度 的(从任一节点出发,通过有限的步骤后必须到达一个终点,显然也不能有环) 那么应该用什么对象做为终点呢? 很显然应该用一个特殊的对象。 Object.prototype 确实是个特殊的对象,咱们先假设用它作终点,那么考虑一下,当取它的原型时应该怎么办? 即:
Object.prototype.__proto__
应该返回什么? 固然 JavaScript 已经给了咱们答案,先不考虑已知的答案 取一个对象的属性时,可能发生三种状况:
- 若是属性值存在,那么返回属性的值
- 若是属性不存在,那么返回 undefined
- 无论属性存在与否,有可能抛出异常
综上所述, 咱们已经假设 Object.prototype 是终点了,哪里还能获取属性,因此排除掉 1 。另外抛出异常也不可取,3 排除。 状况 2,它不存在 原型属性了,返回 undefined 呢? 也很差,由于 undefined 一种解释是原型不存在,可是也至关于原型就是 undefined。这样,在原型链上就会存在一个非对象的值。
因此,最佳选择就是 null。一方面,你无法访问 null 的属性,因此起到了终止原型链的做用;另外一方面,null 在某种意义上也是一种对象,即空对象(JavaScript 中之因此存在 null, 就是为了表示空对象的)。这样一来,就不会违反 “原型链 上只有对象”的约定。
因此,“原型链的终点是 null” 虽然不是必须不可的,但确实最合理的。
在 JavaScript 里任何东西都是对象,包括函数,能够称为函数对象。因此 Foo 也是对象,既然是对象,必然有
__proto__
属性,那么函数对象 Foo 的__proto__
又指向了哪里,又是谁的 instance ?JavaScript 里定义了一个特殊的函数叫 Function,能够称做全部函数的爸爸,全部的函数都是它的实例,所以你能够认为,定义 Foo 的时候发生了这样的事情:
var Foo = new Function(args, function_body);
因而:
function Foo() {
}
console.log('Function && Foo:', Function.prototype === Foo.__proto__); // true
console.log(Foo instanceof Function); // true
复制代码
函数 Foo 由特殊的构造函数 Function 构建,因此 Function.prototype 与 Foo.
__proto__
指向同一个对象,也就是函数的 原型注意这里的 Function.prototype,这也是 JavaScript 里一个特殊的对象,Chrome 的 console 里要是输入 Function.prototype,根本什么也打印不出来,什么 native code,就是说它是 内部实现 的。
走到这一步,Function.prototype 并无走到头,咱们继续探索 Function.prototype.
__proto__
, 能够发现 ··TMD·· 居然是 Object.prototype,因而:
Object.prototype['fooName'] = 'HAHAHA'
function Foo() {
}
console.log('Function 的原型的原型:', Function.prototype.__proto__ === Object.prototype); // true
console.log(Foo.fooName); // HAHAHA
复制代码
那么问题来了,Function 本身呢? 它其实也是个函数,也是个对象,它的
__proto__
指向谁? 答案是它本身的 prototype因而:
function Foo() {
}
console.log(Function.__proto__ === Function.prototype); // true
复制代码
So:全部的函数都是 Function 的 instance, Function本身也是它本身的实例,不事后者严格来讲并不许确,Function 并非它本身创造本身的,而应该看做 JavaScript 里原生的一个函数对象,只不过它的
__proto__
指向了它本身的 prototype 而已。
咱们已知通常任何对象都是 Object 的 instance, 由于原型链的顶端都指向了 Object.prototype。 那么 Object 自己是什么? Object 也是个函数,而任何函数都是 Function 的实例对象,好比 Array, String,固然 Object 也包括在内,它也是 Function 的实例,因而:
console.log(Object.__proto__ === Function.prototype); // true
console.log(Object instanceof Function) // true
console.log(Function instanceof Object) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
复制代码
WDNMD ??
结合上文中所知的解读上述代码:
已知 Object 是全部对象的构造函数,正由于它是一个函数,因此它是 Function 的实例,故 Object instanceof Function
已知 Function.
__proto__
指向 Function.prototype, 指向同一个 原型 这个原型是一个对象且并非终点,继续获取 Function.prototype.__proto__
指向了 Object.prototype, 故 Function 的原型链的顶端亦是 Object.prototype,而 instance 的查找规则就是原型链上是否有一个 原型 是某个构造函数的 prototype, 因此 Function instanceof Object 成立已知 Object.
__proto__
=== Function.prototype , Function.prototype.__proto__
=== Object.prototype, 故:Object.__proto__
=> Function.prototype.__proto__
=> Object.prototype 故:Object instance Object 成立已知:Function.
__proto__
=== Function.prototype, 故:Function instanceof Function 成立
那么问题来了,Function 和 Object 到底谁先谁后,谁主谁次?因而乎,就有了一个 JavaScript 里常常说到的蛋鸡问题:
Object instanceof Function === true > Function instanceof Object === true
借鉴网上的理解:
首先没鸡没蛋,先有一个特殊对象 root_prototype,它是上帝。
接下来应该是先有 Function,而且定义它的 prototype 和proto,都连上了 root_prototype。
而后才有了 Object,它是 Function 的 instance,继承了 Function。这时候 Object 仍然只是个普通的函数。
而后规定 Object.prototype = root_prototype,这时候 Object 才开始显得特殊,成为了原型链的顶端,不然它和其它函数根本没什么区别。
因而全部的东西,包括 Function,都成了 Object 的 instance 了。
这里要强调 Object 和其它函数的不一样之处。Object 之因此特殊,就是由于 Object 的 prototype 被设定为了 root_prototype,仅此而已;而其它函数例如 foo,它的 prototype 只是一个普通的对象,这个对象的proto默认状况下指向 root_prototype。至于为何这样设定,为何 Object 会特殊化,大概只是由于 Object 这个名字起得好,而 foo,bar 没那么特殊。因此说白了 Object 函数只是一个盛放 root_prototype 的容器而已,从而使它晋升为一个特殊的函数。
另外值得注意的是,obj instanceof function 并不意味着 obj 就是这个 function 建立出来的,只不过是 obj 的原型链上有 function.prototype 而已。
因此所谓的 Object instanceof Function 和 Function instanceof Object 的蛋鸡问题,前者应该来讲是天然而然、无可置疑的,能够认为 Object 函数是 Function 创造出来的;然后者说白了只是由于强行规定了 Object 函数的特殊性,而致使的一个推论,而 Function 并不是是 Object 建立的。
讶羽的博客 - JS 深刻系列 - 从原型到原型链 - github 地址
JavaScript 原型链以及 Object,Function 之间的关系
为何原型链的终点是 null,而不是 Object.prototype?
JavaScript 自定义构造函数存在的问题(为何要使用原型)