创建对象的几种方式:

  1. // 第一种方式:字面量
  2. var o1 = {name: 'o1'};
  3. var o2 = new Object({name: 'o2'});
  4. // 第二种方式:构造函数
  5. var M = function (name) { this.name = name; };
  6. var o3 = new M('o3');
  7. // 第三种方式:Object.create
  8. var p = {name: 'p'};
  9. var o4 = Object.create(p);

上面几种创建对象的方式,打印出如下结果:
image.png

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>原型链</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. // 第一种方式:字面量
  10. var o1 = {name: 'o1'};
  11. var o2 = new Object({name: 'o2'});
  12. // 第二种方式:构造函数
  13. var M = function (name) { this.name = name; };
  14. var o3 = new M('o3');
  15. // 第三种方式:Object.create
  16. var p = {name: 'p'};
  17. var o4 = Object.create(p);
  18. M.prototype.say = function () {
  19. console.log('say hi');
  20. };
  21. var o5 = new M('o5');
  22. var new2 = function (func) {
  23. var o = Object.create(func.prototype);
  24. var k = func.call(o);
  25. if (typeof k === 'object') {
  26. return k;
  27. } else {
  28. return o;
  29. }
  30. };
  31. </script>
  32. </body>
  33. </html>

构造函数、原型、实例、原型链

image.png

  1. 构造函数是通过 new 运算符来生成一个实例对象;
  2. 构造函数也是函数,任何函数都可以作为构造函数;
  3. 函数都有一个 prototype 属性。JS 引擎会在你声明一个函数时自动加上这一个属性( prototype );

原型链:每一个实例对象都有一个 __proto__属性,它指向的是这个对象的原型对象,原型对象也是一个对象,它也会有 __proto__属性,这样就形成了一个链条,称之为 原型链。

  1. M.prototype.constructor === M // true
  2. o3.__proto__ === M.prototype // true

总结

  1. 函数才会有 prototype属性,对象是没有的;
  2. 只有实例对象才会有 __proto__属性。但是函数也有 __proto__属性,是为什么呢???因为函数也是一个对象,所以它也会有__proto__属性。M函数也是 Function 构造函数的一个实例对象。

    1. M.__proto__ === Function.prototype // true
  3. 实例是通过构造函数来生成的,它是怎么和原型对象产生联系的呢???其实就是通过构造函数上的 prototype属性。

    instanceof 的原理

    instanceof运算符:左操作数是一个对象,右操作数是标识对象的类。若左对象是右对象的实例,则表达式返回 true,否则返回 false
    image.png
    实例对象是通过构造函数来生成。
    实例对象上的__proto__属性,是通过构造函数上的 prototype属性来和原型进行关联的。
    instanceof 原理:实例对象的 __proto__属性和构造函数的 prototype属性是否是同一个引用。
    注意:只要是原型链上的都会看成是实例对象的构造函数。 ```javascript // 代码延用上侧代码举例

o3 instanceof M // true

o3 instanceof Object // true

o3.proto === M.prototype // true M.prototype.proto === Object.prototype // true

  1. 虽然`Object `不是` o3`的构造函数,但是它存在于原型链上,所以说用 `instanceof `来判断 `o3`的构造函数是不是 `Object `是不准确的。怎么准确判断,方法如下:
  2. ```javascript
  3. o3.__proto__.constructor === Object // false

再次举例说明:当通过 instanceof来判断一个对象是否是一个类的实例时,这个判断也会包含对父类的检测。虽然 instanceof的右操作数是构造函数,但计算过程实际是检测了对象的继承关系。
代码举例如下:

  1. var d = new Date();
  2. d instanceof Date; // true d是Date的实例
  3. d instanceof Object; // true 所有对象都是Object的实例

追加知识

  1. L instanceof R
  2. // instanceof运算时,通过判断L的原型链上是否存在R.prototype,若存在返回 true ,否则返回 false
  3. // L.__proto__.__proto__.__proto__.__proto__... === R.prototype

具体可查看 手写 instanceof
instanceof运算规则为:instanceof 检测左侧的 proto 原型链上,是否存在右侧的 prototype 原型。

图解构造器 FunctionObject关系:

TODO:图
原型链 - 图4

  1. // 1:构造器 Function 的构造函数是自身
  2. // Function.constructor ===> ƒ Function() { [native code] }
  3. Function.constructor === Function // true
  4. // 2:构造器 Object 的构造函数是 Function(由此可知:所有构造器的构造函数都指向 Function)
  5. // Object.constructor ===> ƒ Function() { [native code] }
  6. // Array.constructor ===> ƒ Function() { [native code] }
  7. Object.constructor === Function // true
  8. // 3:构造器 Function 的 __proto__ 是一个特殊的匿名函数
  9. Function.__proto__ // ƒ () { [native code] }
  10. // 4:这个特殊匿名函数的 __proto__ 指向 Object 的 prototype 原型
  11. Function.__proto__.__proto__ === Object.prototype // true
  12. // 5:Object 的 __proto__ 指向 Function 的 prototype,也就是 3 中说的特殊匿名函数
  13. Object.__proto__ === Function.prototype // true
  14. Function.prototype === Function.__proto__ // true
  15. Object.__proto__ === Function.__proto__ // true

当构造器 Object、Function 用 instanceof来比较

  1. // why??????
  2. Object instanceof Function // true
  3. Function instanceof Object // true
  4. // 答案:依据 instanceof 的运算规则就可明白
  5. Object.__proto__ === Function.prototype
  6. Function.__proto__.__proto__ === Object.prototype // true

小结:

  1. 所有构造器的构造函数(constructor)都指向 Function
  2. Functionprototype指向一个特殊的匿名函数,此匿名函数的__proto__指向 Object.prototype。即:Function.prototype.__proto__ === Object.prototype的结果为 true
  3. Objectnew出来实例对象的基类,function Object() { [native code] }
  4. Function是作为众多 function出来的函数的基类,function Function() { [native code] }
  5. 构造函数的 __proto__(包括FunctionObject)都指向Function.prototype
  6. 原型对象的 __proto__都指向Object.prototype
  7. Object.prototype.__proto__ === null
  8. 所有对象(包括:对象、函数)都有__proto__属性,指向构造该对象的构造函数的原型;
  9. 只有函数function才具有prototype属性。此属性是一个指针,指向一个对象,此对象的用途就是包含所有实例共享的属性和方法(此对象称为:原型对象)。原型对象也有一个属性,叫做 constructor,这个属性包含一个指针,指回原构造函数。

    new 运算符

    image.png
    模拟 new 运算符:
    1. var new2 = function (func) {
    2. // func 是一个构造函数
    3. var o = Object.create(func.prototype);
    4. var k = func.call(o);
    5. if (typeof k === 'object') {
    6. return k;
    7. } else {
    8. return o;
    9. }
    10. };
    1. o6 = new2(M) // M
    2. o6 instanceof M // true
    3. o6 instanceof Object // true
    4. o6.__proto__.constructor === M // true
    注意:Object.create()创建对象的对象是用原型链来连接的,o4 的 __proto__指向这个p对象 ,也就是指向构造函数的 prototype属性,o4.__proto__ === p // true

    怎么手写一个 new 函数???

    isPrototypeOf 、instanceof、hasOwnProperty 三者区别

    isPrototypeOf

    作用:检测一个对象是否是另一个对象的原型。或者说 一个对象是否被包含在另一个对象的原型链中。 ```javascript var p = {x:1};//定义一个原型对象

var o = Object.create(p);//使用这个原型创建一个对象

p.isPrototypeOf(o);//=>true:o继承p

Object.prototype.isPrototypeOf(p);//=> true p继承自Object.prototype

  1. 上例摘自《Javascript 权威指南》,简单解释以下就是:每一个 JS 对象都和原型关联,每一个对象都从原型继承属性。所有通过对象 **直接量** 创建的对象都使用 `Object.prototype` 为他们的原型,因此`p`是继承自 `Object.prototype` ,因此在`p`的原型链中一定存在 `Object.prototype`。<br />`Object.create()` 方法是创建一个新对象,第一个参数是这个新对象的原型。所以上例 创建的 `o`对象,它的原型是 `p`
  2. ```javascript
  3. function Animal(){
  4. this.species = "动物";
  5.  };
  6. var eh = new Animal();
  7. Animal.prototype.isPrototypeOf(eh) //=>true

上例是通过 new创建了实例对象 eh,使用构造函数 Animalprototype作为它(eh)的原型。

综上两例,发现调用 isPrototypeOf的三种方式如下:

  1. p.isPrototypeOf(o); // true
  2. Object.prototype.isPrototypeOf(p); // true
  3. Animal.prototype.isPrototypeOf(eh); // true

**isPrototypeOf** 总结如下:

  1. 通过 Object.create()方法创建的对象,使用第一个参数作为其原型;
  2. 通过对象 直接量 的对象,使用 Object.prototype作为其原型;
  3. 通过 new创建的对象,使用构造函数的 prototype属性作为其原型。

    isPrototypeOf 和 instanceOf 比较

    ```javascript // A构造函数的prototype对象是否在B的原型链上 B instanceof A

// A对象是否在B的原型链上 A.isPrototypeOf(B)

  1. 举例:
  2. ```javascript
  3. class A{}
  4. class B extends A{}
  5. let b = new B();
  6. // 这两个类之间的关系
  7. B.__proto__ === A //true
  8. B.prototype.__proto__=== A.prototype //true
  9. b.__proto__ === B.prototype //true

为什么会出现如下代码结果。
首先类的继承是按如下方式实现的:

  1. class A {
  2. }
  3. class B {
  4. }
  5. // B的实例继承A的实例
  6. Object.setPrototypeOf(B.prototype, A.prototype);
  7. const b = new B();
  8. // B的实例继承A的静态属性
  9. Object.setPrototypeOf(B, A);
  10. const b = new B();

setPrototypeOf()实现如下:

  1. Object.setPrototypeOf = function (obj, proto) {
  2. obj.__proto__ = proto;
  3. return obj;
  4. }

所以得出前两个结果,也就是如下代码结果:

  1. // 这两个类之间的关系
  2. B.__proto__ === A //true
  3. B.prototype.__proto__ === A.prototype //true

new 操作是按如下方式实现的:

  1. var b = new Object();
  2. b.__proto__ = B.prototype;
  3. B.call(b);

所有得到第三个表达式: b.__proto__ === B.prototype //true
接下来看看 instanceofisPrototypeOf的表现:

  1. A.isPrototypeOf(B);//true
  2. A.isPrototypeOf(b);//false
  3. b instanceof B;//true
  4. b instanceof A;//true
  5. B instanceof A;//false

再次回头看看三个判断表达式给出的结果就可知:
A对象在B的原型链上,不在b的原型链上,所以:

  1. A.isPrototypeOf(B);//true
  2. A.isPrototypeOf(b);//false

A,B的prototype在b的原型链上而不在B的原型链上,所以:

  1. b instanceof B;//true
  2. b instanceof A;//true
  3. B instanceof A;//false

小结:
Y instanceof X判断是的是Xprototype是否在Y的原型链上,而实例的原型链(__proto__)指向的就是其构造函数的prototype,即Y instanceof X判断Y是否是X的一个实例(若YX的实例,那他也是X父类的实例)。
X.isPrototypeOf(Y)判断的是X对象是否在Y的原型链上,同样Y继承X的关系是X对象在Y对象的原型链上,即X.isPrototypeOf(Y)判断X是否继承至Y

hasOwnProperty

对象的 hasOwnProperty() 方法用来检测该属性是否是对象的自有属性。若是对象的自有属性,则返回 true,否则返回 falsefalse说明该属性是继承属性。

  1. function Animal(){}//定义Animal构造函数
  2. Animal.prototype = {//定义Animal原型
  3. species:"动物",
  4. say:function(){
  5. console.log('i can say word');
  6. }
  7. }
  8. function Cat(name,color){//定义构造函数Cat
  9. this.name = name;
  10. this.color = color;
  11. }
  12. var F = function(){};
  13. F.prototype = Animal.prototype;
  14. Cat.prototype = new F();
  15. Cat.prototype.constructor = Cat;// Cat继承Animal 用F空对象作为媒介
  16. var eh = new Cat('lili','white');//实例化对象
  17. console.log('say' in eh)//=>true
  18. console.log('name' in eh)//=>true
  19. console.log('color' in eh)//=>true
  20. console.log('species' in eh)=>true
  21. console.log(eh.hasOwnProperty('say'))=>false 由于say为继承属性 非自有属性
  22. console.log(eh.hasOwnProperty('species'))=>false 由于species为继承属性 非自有属性
  23. console.log(eh.hasOwnProperty('name'))=>true
  24. console.log(eh.hasOwnProperty('color'))=>true
  25. for(var key in eh){
  26. console.log(key);
  27. if(eh.hasOwnProperty(key)){
  28. console.log(key) //=>species say name color
  29. }
  30. }

原型链总图:

原型链图解.jpg