原型和原型链

文章目录

    • 前言
      • 一、构造函数
      • 二、原型和原型链
          • 1、原型的概念
          • 2、原型链的概念
          • 总结
      • 三、创建对象的方式
          • 1、字面量
          • 2、构造函数的调用
          • 3、Object.create()
          • 4、__proto__和prototype
      • 四、Javascript如何实现继承
          • 1.构造函数绑定
          • 2.实例继承
          • 3.拷贝继承
          • 4.原型继承
          • 5.ES6 extends

前言

由于ES6之前没有引入类(class)的概念,javascript 并非直接通过,而是直接通过构造函数的方式常见实例,所以有必要先复习下构造函数

一、构造函数

构造函数模式的目的是为了创建一个自定义类,并创建这个类的实例,构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立的,即实例识别。

构造函数本身也是一个普通的函数,创建方式和普通函数没有区别,旦构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。


原型和原型链_第1张图片

每创建一个Person构造函数,在Person构造函数中,为每一个对象都添加了一个sayHello方法,也就是说构造函数每执行一次就会创建一个新的sayHello方法。这样就导致了构造函数执行一次就会创建一个新的方法,执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的,为什么不把这个方法单独放到一个地方,并让所有的实例都可以访问到呢?这就需要原型(prototype)

用一张图表示构造函数和实例原型之间的关系:原型和原型链_第2张图片
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

二、原型和原型链

1、原型的概念
  • JavaScript的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是自身的原型
  • JavaScript的函数对象,除了原型 [proto] 之外,还有 prototype 属性,当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [proto]
2、原型链的概念

当一个对象调用自身不存在的属性/方法时,就会去自己 [proto] 关联的前辈 prototype 对象上去找,如果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”。

总结

JavaScript中的对象,都有一个内置属性[Prototype],指向这个对象的原型对象。当查找一个属性或方法时,如果在当前对象中找不到,会继续在当前对象的原型对象中查找;如果原型对象中依然没有找到,会继续在原型对象的原型中查找(原型也是对象,也有它自己的原型);直到找到为止,或者查找到最顶层的原型对象中也没有找到,就结束查找,返回undefined。这个查找过程是一个链式的查找,每个对象都有一个到它自身原型对象的链接,这些链接组建的整个链条就是原型链。拥有相同原型的多个对象,他们的共同特征正是通过这种查找模式体现出来的。
在上面的查找过程,我们提到了最顶层的原型对象,这个对象就是Object.prototype,这个对象中保存了最常用的方法,如toString、valueOf、hasOwnProperty等,因此我们才能在任何对象中使用这些方法。

三、创建对象的方式

1、字面量

当通过字面量方式创建对象时,它的原型就是Object.prototype。虽然我们无法直接访问内置属性[[Prototype]],但我们可以通过Object.getPrototypeOf()或对象的__proto__获取对象的原型。

var obj = {};
Object.getPrototypeOf(obj) === Object.prototype;   // true
obj.__proto__  === Object.prototype;            // true
2、构造函数的调用

前面已经介绍了构造函数,它也是一种常用的创建对象的方式。基于同一个函数创建出来的对象,理应可以共享一些相同的属性或方法,但这些属性或方法如果放在Object.prototype里,那么所有的对象都可以使用它们了,作用域太大,显然不合适。于是,JavaScript在定义一个函数时,同时为这个函数定义了一个 默认的prototype属性,所有共享的属性或方法,都放到这个属性所指向的对象中。由此看出,通过一个函数的构造调用创建的对象,它的原型就是这个函数的prototype指向的对象。

var aa = function(name) { this.name = name };
aa.prototype.getName = function() { return this.name; }   //在prototype下存放所有对象的共享方法
var obj = new aa('JavaScript');
obj.getName();                  // JavaScript
obj.__proto__ === aa.prototype;  // true
3、Object.create()

第三种常用的创建对象的方式是使用Object.create()。这个方法会以你传入的对象作为创建出来的对象的原型。

var obj = {};
var obj2 = Object.create(obj);
obj2.__proto__ === obj;       // true

这种方式还可以模拟对象的“继承”行为。

function Foo(name) {
	this.name = name;
}

Foo.prototype.myName = function() {
	return this.name;
};

function Bar(name,label) {
	Foo.call( this, name );   //
	this.label = label;
}

// temp对象的原型是Foo.prototype
var temp = Object.create( Foo.prototype );  

// 通过new Bar() 创建的对象,其原型是temp, 而temp的原型是Foo.prototype,
// 从而两个原型对象Bar.prototype和Foo.prototype 有了"继承"关系
Bar.prototype = temp;

Bar.prototype.myLabel = function() {
	return this.label;
};

var a = new Bar( "a", "obj a" );

a.myName(); // "a"
a.myLabel(); // "obj a"
a.__proto__.__proto__ === Foo.prototype;  //true
4、__proto__和prototype

__proto__指向当前对象的原型,prototype是函数才具有的属性,默认情况下,new 一个函数创建出的对象,其原型都指向这个函数的prototype属性。
原型和原型链_第3张图片
每个实例对象(object )都有一个私有属性(称之为 proto)指向它的原型对象(prototype)。该原型对象也有一个自己的原型对象 ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

四、Javascript如何实现继承

1.构造函数绑定

使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上

function Cat(name,color){
  Animal.apply(this, arguments);
  this.name = name;
  this.color = color;
}
2.实例继承

将子对象的 prototype 指向父对象的一个实例

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
3.拷贝继承

把父对象的属性和方法拷贝给子对象

function extend(Child, Parent) {
     var p = Parent.prototype;
     var c = Child.prototype;
     for (var i in p) {
        c[i] = p[i];
     }
     c.uber = p;
    }
4.原型继承

将子对象的 prototype 指向父对象的 prototype

function extend(Child, Parent) {
        var F = function(){};
       F.prototype = Parent.prototype;
       Child.prototype = new F();
       Child.prototype.constructor = Child;
       Child.uber = Parent.prototype;
    }
5.ES6 extends

class ColorPoint extends Point {}

 class ColorPoint extends Point {
       constructor(x, y, color) {
          super(x, y); // 调用父类的constructor(x, y)
          this.color = color;
       }
       toString() {
          return this.color + ' ' + super.toString(); // 调用父类的toString()
       }
    }

你可能感兴趣的:(js基础)