原型 原型链 new操作 继承

323 阅读3分钟

几个定义

function Person(){}
var person = new Person();
  • 构造函数(Person):就是专门用来生成实例对象的函数;
  • 原型:Person.prototype就是原型,它是一个对象,也称它为原型对象;作用就是共享属性和方法;
  • 原型链:原型与原型层层链接的过程;

每一个构造函数(Person)都有一个原型对象(prototype);每个原型对象都包含一个指向构造函数的指针(constructor);每个实例都包含一个指向原型的内部指针(__proto__)。

Person.prototype === person.__proto__ === new Person().__proto__
Person.prototype.constructor === person.__proto__.constructor === Person

原型链图

原型链查找方法

如果试图引用对象(实例person)的某个属性或方法:

(1)首先在实例对象内部寻找该,直至找不到;

(2)接着在该对象的原型(person.__proto__)里去找这个属性;

(3)然后会在上级的原型对象里去找...直至Object的原型对象

new过程

(1)创建一个空对象;

(2)将该空对象的原型指向构造函数的原型(obj.__proto__=Person.prototype);

(3)重新绑定this,使构造函数的this指向新对象(Person.call(this));

(4)开始执行构造函数内部代码。

手动实现new操作:

function createNew() {
    // 1. Array.from(arguments).shift();转换成数组使用数组的方法shift将第一项弹出
    // 2.[].shift().call(arguments); // 通过call() 让arguments能够借用shift方法
    let Constructor = Array.from(arguments).shift();
    //创建一个空对象
    let obj = {};
    //将该空对象的原型属性指向构造函数的原型
    obj.__proto__ = Constructor.prototype;
    //将构造函数的this指向该空对象
    Constructor.call(obj, ...arguments);
    return obj;
}

new命令总是返回一个对象,如果构造函数内部有return一个对象,new命令会返回return语句指定的对象;否则,返回this对象; 对普通函数(内部没有this关键字的函数)使用new命令,则会返回一个空对象

继承

1、原型链继承

基本思想: 将父类的实例作为子类的原型

不足:1.无法实现多继承;2.来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的);3.创建子类实例时,无法向父类构造函数传参

function Person(name) {
	this.name = name;
	this.sleep = function() {
		return this.name + ' is sleeping';
	}
}
Person.prototype.eat = function(food) {
	return this.name + ' is eating ' + food;
}
function Strive() {}
Strive.prototype = new Person('Strive');

//使用
let strive = new Strive();
console.log(strive.name);    //Strive
console.log(strive.sleep()); //Strive is sleeping
console.log(strive.eat('apple'));//Strive is eating apple

2、构造继承

基本思想:在子类型构造函数的内部调用超类型构造函数.

不足:1.只能继承父类的实例属性和方法,不能继承原型属性/方法;2.无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

function Strive() {
    Person.call(this);
    //other code
}

3、实例继承

基本思想:为父类实例添加新特性,作为子类实例返回

function Strive() {
    var instance = new Person();
    instance.attr = '';
    instance.method = function(){};
    return instance;

5、组合继承 基本思想:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承

不足:调用了两次父类构造函数,生成了两份实例

function Strive() {
    Person.call(this);
    //other code
}
Strive.prototype = new Person();
Strive.prototype.constructor = Strive;

6、寄生组合继承

function Strive() {
    Person.call(this);
    //other code
}
Strive.prototype = Object.create(Person.prototype);
Strive.prototype.constructor = Strive;

7.es6 class实现继承

class Person {
	constructor(name) {
		this.name = name;
	}
	sleep() {
		return this.name + ' is sleeping';
	}
}
class Strive extends Person {
	constructor(name) {
		super(name);
	}
	eat(food) {
		return this.name + ' is eating ' + food;
	}
}
let strive = new Strive('Strive');

类和构造函数的区别:

  • 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行;
  • 类的所有实例共享一个原型对象;
  • 类的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。

Object.create(proto, propertiesObject)

使用指定的原型proto对象及其属性propertiesObject去创建一个新的对象,proto 是必填参数,就是新创建出来的对象的原型 (新对象的 __proto__属性指向的对象)

手动实现

var objectCreate = function(proto) {
    function Fn(){}
    Fn.prototype = proto;
    retuen new Fun();
}

Object.create与new的区别