You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.
Number.__proto__ === Function.prototype // true
Number.constructor == Function //true
Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true
String.__proto__ === Function.prototype // true
String.constructor == Function //true
// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Object.__proto__ === Function.prototype // true
Object.constructor == Function // true
// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true
Array.__proto__ === Function.prototype // true
Array.constructor == Function //true
RegExp.__proto__ === Function.prototype // true
RegExp.constructor == Function //true
Error.__proto__ === Function.prototype // true
Error.constructor == Function //true
Date.__proto__ === Function.prototype // true
Date.constructor == Function //true
JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。它们的 __ proto __ 是Object.prototype。
function Person() {
}
Person.prototype.name = 'Kevin';
var person = new Person();
person.name = 'Daisy';
console.log(person.name) // Daisy
delete person.name;
console.log(person.name) // Kevin
从上面的例子中可以得出:
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性(原型对象),如果还查不到,就去找原型的原型,一直找到最顶层为止,最终的结果就是 null, 表示没有对象,即该处不应该有值。**在这个查找过程中,由相互关联的原型组成的链状结构就构成了原型链。**注意这个链状结构是由 __ proto __ 连接起来的。
Object.prototype.__ proto __ === null,保证了原型能够正常结束。
八、原型继承
原型链是实现继承的主要方法 。
基本思路:
利用原型让一个引用类型继承另一个引用类型的属性和方法。
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数想指针(constructor),而实例对象都包含一个指向原型对象的内部指针( __ proto __ )。如果让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针( __ proto __ ),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是另一个类型的实例……这就构成了实例与原型的链条。
The text was updated successfully, but these errors were encountered:
Uh oh!
There was an error while loading. Please reload this page.
在说原型 和 原型链 之前,需要先弄清楚对象的分类及区别
一、对象的分类:普通对象 与 函数对象
在JS 中,万物皆对象,但是对象也是有区别的,分别为 普通对象 和 函数对象,Object, Function, Array, String, Number, Boolean, Date 等都是 JS 内置的函数对象。举例说明
在上面的例子中 o1, o2,o3 是普通对象,f1,f2,f3 是函数对象。那么怎么来区分他们呢? 是这样的:凡是通过 new Function() 创建的对象都是函数对象,其他的对象都是普通对象。注意 f1, f2 归根到底也是通过 new Function() 创建的,上面的写法只是一个语法糖,所以他们也是函数对象,也可以说任意一个函数都是函数对象,函数对象就是函数。另外,Object, Function, Array, String, Number, Boolean, Date 这些 JS 内置函数对象也是通过 new Function() 创建的。
一定要分清楚普通对象和函数对象,这样利于下面的理解。
二、构造函数
先想一下基本的构造函数的内容:
在这个例子中,person1, person2 都是 Person 的实例,这两个实例都有一个 constructor(构造函数)属性,该属性(是一个指针)指向 他们的构造函数 Person, 即:
这里要记住两个概念: 构造函数,实例:
person1 和 person2 都是构造函数 Person 的实例
以及最重要的一点:实例 的 构造函数属性(constructor)指向 构造函数本身
在 JS 中,构造函数本身首先是一个函数,可以说任意的一个函数 都可以当作构造函数,但是一般在定义自定义构造函数时,要将函数名首字母改用大写的形式,以跟普通函数区分,形式就是上面的 Person 构造函数的形式。
另外,JS 的内置函数 Object, Function, Array, String, Number, Boolean, Date 等都是构造函数,跟 Person 差不多,只不过他们只是 JS 内部 出于创建 特定类型的 对象 而自定义的,他们又叫构造器,都是函数对象。
三、原型对象
在 JS 中,每当定义一个对象(函数也是对象),对象中都会包含一些预定义的属性。其中每个 函数 (对象) 都会有一个 prototype 属性(是一个指针),它指向 一个对象-- 通过该函数创建的实例对象的原型,所以叫原型对象。在上面的例子中就是 person1, person2 的原型。
那什么是原型呢?可以这样理解:每一个 JavaScript 对象(null 除外)在创建的时候就会与之关联一个对象,这个对象就是我们所说的原型,它包含了特定类型的所有实例共享的属性和方法,每一个对象都会从原型继承这些属性、方法。所以,原型对象的好处是:可以让所有的实例对象共享它所包含的属性和方法。当然,我们也可以对这个对象进行扩展,比如:
Person.prototype 就是原型对象,上面的 Person.prototype.xxx 这些操作是 我们自己 对原型对象进行的 扩展,目的就是 Person 构造函数的所有实例都可以 继承 这些属性。
用到上面的例子就是: Person.prototype.constructor === Person.
在第二部分中有:person1.constructor === Person。
他们好像有点联系:
在第二部分,我们知道 实例的构造函数属性(constructor)指向构造函数,person1 有 constructor 属性,是因为 person1 是 Person 的实例。
那么,Person.prototype 为什么也有 constructor 属性呢?结合下面的信息:
我们可以得出: Person.prototype 也是 Person 的实例对象。也就是说:
原型对象(Person.prototype)就是 构造函数(Person) 的一个实例对象,相当于在构造函数创建的时候,自动创建了一个它的实例,并它这个实例赋值给了它的(构造函数) prototype 属性,基本过程大致如下:
从而可以知道,原型对象 其实就是 普通对象。但是,注意一个例外:Function.prototype ,它是函数对象,但它也很特殊,没有 prototype 属性(前面说了所有的函数对象都有 prototype 属性)。看例子:
Function.prototype 为什么会是函数对象呢?
因为:原型对象是构造函数的一个实例,而这个实例是通过 new Function() 方式创建的,第一部分 提到了:凡是通过 new Function() 方式创建的对象都是函数对象,所以Function.prototype 是函数对象。
那么,原型对象 的主要作用是什么呢,存在什么问题吗?
原型对象的主要作用是 :用于继承,对特定类型预定义一些相关的属性和方法,让所有的实例都可以使用 原型对象包含的属性和方法。这个共享的特性有很多的好处,同时也存在一个缺点:
原型对象的最大的问题就是由其共享的本性导致的。某一个实例对象比较特殊,需要修改它的原型对象的某个属性,如果修改了原型对象,那么所有的实例的这个属性都会跟着修改,。
有上述例子可以看出,当一个实例对象 修改或赋值 一个属性时,如果此对象本身没有此属性,就会给对象加上此属性,如果有就会修改此属性,修改了实例的属性不会影响其他实例的属性;如果修改了原型对象的属性,则所有的实例访问此属性时都会跟着修改。
如何解决这个问题呢?可以组合使用 构造函数模式 和 原型模式,将值不同的属性放在构造函数中,而不是原型对象中。
还有一种问题就是:一个构造函数的多个实例中,他们使用 原型对象中的某一个方法返回值是一样的,只有一个特殊的实例使用这个方法时需要其他的返回值,甚至多个实例都有一个方法与其他实例不通过,需要怎么处理呢?
我的想法是使用默认参数
这里需要注意的是:当以参数列表形式设置默认值时,赋值方式是按序赋值的,因为多数实例都使用默认参数,有特例属性的地方才需要传值,所以这里传值时要使用对象,然后解构取值
原型对象还有一个作用:就是减少内存占用。在 JS 中,除了 原型对象 的属性和方法之外其他的属性方法都是独立的。就是说, 如果复创建了多个对象,那么每个对象中的方法都会在内存中开辟新的空间,这样浪费的空间就比较多。
而原型对象中的属性方法就不一样了,因为原型对象是所有实例共享的。所有的实 8000 对象都共享这个原型对象,而不会开辟新的空间。
四、 __ proto __
JS 在创建对象(不论是普通对象还是函数对象)后,实例都会有一个叫做 __ proto __ 的属性(内部指针),指向创建它的构造函数的原型对象。
在 Person 的例子中就是:person1.__ proto __ === Person.prototype
请看下图:
《JavaScript 高级程序设计》的图 6-1
根据这个图,我们可以知道:
这里需要明确的一点是,这个连接存在于实例(person1)与 构造函数(Person)的原型对象(Person.prototype)之间,而不是存在于 实例(person1)和 构造函数(Person)之间。
注意: ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox、Safari和Chrome在每个对象上都支持一个属性__ proto __ , 现在 绝大部分浏览器都支持__ proto __属性,所以它才被加入了 ES6 里(ES5 部分浏览器也支持,但还不是标准)。
五、函数对象
所有函数(对象)的 __ proto __ 都指向 Function.prototype,它是一个空函数(Empty function)
JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。它们的 __ proto __ 是Object.prototype。
上面说的函数对象当然包括自定义的。如下
这说明什么呢?
所有的构造器都来自于 Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了 Function.prototype 的属性及方法。如length、call、apply、bind
Function.prototype也是唯一一个typeof XXX.prototype为 function的prototype。其它的构造器的prototype都是一个对象(原因第三节里已经解释过了)。如下(复习一遍):
知道了所有构造器(含内置及自定义)的原型 __ proto __ 都是Function.prototype,那Function.prototype的__ proto __ 是谁呢?
相信都听说过JavaScript中函数也是一等公民,那从哪能体现呢?如下
console.log(Function.prototype.__ proto __ === Object.prototype) // true
这说明所有的构造器也同时是一个JS 对象,可以给构造器添加/删除属性等。同时它也继承了Object.prototype上的所有方法:toString、valueOf、hasOwnProperty等。
最后Object.prototype的 __ proto __ 是谁?
Object.prototype.__ proto __ === null // true
已经到顶了,为null。
六、prototype
——《JavaScript 高级程序设计》第三版 P116
我们知道 JS 内置了一些方法供我们使用,比如:
对象可以用 constructor/toString()/valueOf() 等方法;
数组可以用 map()/filter()/reducer() 等方法;
数字可用用 parseInt()/parseFloat()等方法;
当我们创建一个对象时:
var Person = new Object()
Person 是 Object 的实例,所以 Person 继承了Object 的原型对象Object.prototype上所有的方法:
Object 的每个实例都具有以上的属性和方法。
所以我可以用 Person.constructor 也可以用 Person.hasOwnProperty。
当我们创建一个数组时:
var num = new Array()
num 是 Array 的实例,所以 num 继承了Array 的原型对象Array.prototype上所有的方法:
内容多有点乱, 可以用一个 ES5 提供的方法:Object.getOwnPropertyNames , 获取所有**(包括不可枚举的属性)**的属性名, 不包括 prototype 中的属性,返回一个数组:
所以我们知道了,我们创建的数组为啥能用那么多方法了
但是,Object.getOwnPropertyNames(arrayAllKeys) 输出的数组里并没有 hasOwnProperty 等对象的方法。但是随便定义的数组也能用这些方法:
为什么?因为Array.prototype 虽然没这些方法,但是它有原型对象(__ proto __):
所以 Array.prototype 继承了对象的所有方法,当你用num.hasOwnProperty()时,JS 会先查一下它的构造函数 (Array) 的原型对象 Array.prototype 有没有有hasOwnProperty()方法,没查到的话继续查一下 Array.prototype 的原型对象 Array.prototype.__ proto __有没有这个方法。
当我们创建一个函数时:
七、原型链
先看几个问题
从上面的例子中可以得出:
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性(原型对象),如果还查不到,就去找原型的原型,一直找到最顶层为止,最终的结果就是 null, 表示没有对象,即该处不应该有值。**在这个查找过程中,由相互关联的原型组成的链状结构就构成了原型链。**注意这个链状结构是由 __ proto __ 连接起来的。
还有点疑惑:
Object.__ proto __ === Function.prototype // true
Object 是函数对象,是通过 new Function() 创建的,所以 Object.__ proto __ 指向 Function.prototype。
Function.__ proto __ === Function.prototype // true
Function 也是函数对象,也是通过 new Function() 创建的,所以,Function.__ proto __ 指向 Function.prototype。
Function.prototype.__ proto __ === Object.prototype // true
对于这一点,也是非常困惑,不知道下面的说法可否。
Function.prototype 本身是函数对象(空函数),理论上它的 __ proto __ 属性应该指向 Function.prototype, 这样就成了自己指向自己,自己指向自己,没有意义,而且可能造成死循环。
JS 也强调万物皆对象,函数对象也是对象,所以给他认个祖宗,指向 Object.prototype.
Object.prototype.__ proto __ === null,保证了原型能够正常结束。
八、原型继承
原型链是实现继承的主要方法 。
基本思路:
利用原型让一个引用类型继承另一个引用类型的属性和方法。
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数想指针(constructor),而实例对象都包含一个指向原型对象的内部指针( __ proto __ )。如果让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针( __ proto __ ),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是另一个类型的实例……这就构成了实例与原型的链条。
The text was updated successfully, but these errors were encountered: