创建对象、原型链和继承方式

179 阅读3分钟

创建对象的方式

1. 字面量

var o1 = { name: 'o1' };
var o2 = new Object({ name: 'o2' });

2. 通过构造函数

var M = function(name) {  this.name = name;}
var o3 = new M('o3');

3. Object.create()

使用Object.create(p)方法创建的对象,原型对象是参数
var p = { name: 'o4' };
var o4 = Object.create(p);

三种创建方式的结果展示:

![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/7/30/1739da54c110dc02~tplv-t2oaga2asx-image.image)

原型链

1. 构造函数、实例以及原型对象的关系

var M = function(name) {  
    this.name = name;
}
var o3 = new M('o3');
console.log(o3);
console.log(o3.constructor === M);  // true
console.log(o3.__proto__ === M.prototype);  // true
console.log(M.prototype.constructor === M)  // true

2. instanceof

作用:检测原型

原理:判断实例对象的__proto__ 和 构造函数的prototype 是不是同一个引用

var M = function(name) {
    this.name = name;
}
var o3 = new M('o3');
console.log(o3 instanceof M);  // true
console.log(o3 instanceof Object);  // true
console.log(o3.__proto__ === M.prototype);  // true
console.log(M.prototype.__proto__ === Object.prototype);  // true

3. constructor

作用:区分一个对象具体是谁实例化的

var M = function(name) {
    this.name = name;
}
var o3 = new M('o3');
console.log(o3.constructor === M); // true
console.log(o3.constructor === Object); // false

类的声明

/**  * 类的声明  */function Animal() {    this.name = 'name';
}/**  * ES6中class声明  */class Animal2 {  constructor() {    this.name = 'name';  }}

/**  * 实例化  */
console.log(new Animal(), new Animal2())

类的继承

1. 借助构造函数实现继承

原理:改变了Parent1中的this指向

缺点:Parent1原型链上的方法并没有被Child1继承,只实现了部分继承

/**  
  * 借助构造函数实现继承(实现了部分继承)  
  * 原理:改变了Parent1中的this指向,  
  * 缺点:Parent1原型链上的方法并没有被Child1继承  
  */
function Parent1() {
    this.name = 'Parent1';
}
Parent1.prototype.say = function() {};
function Child1() {
    Parent1.call(this); // apply 改变this指向
    this.type = 'Child1';
}
var s1 = new Child1();
console.log(s1); // Child1 {name: "Parent1", type: "Child1"}
console.log(s1.constructor === Parent1) // true

2. 借助原型链实现继承

缺点:假如实例化两个对象,改变其中一个,另一个也会跟着变化

原因:因为原型链中的原型对象是共用的,s2_1.proto = s2_2.proto

/**  
  * 借助原型链实现继承  
  * 缺点:假如实例化两个对象,改变其中一个,另一个也会跟着变化
  * 原因:因为原型链中的原型对象是共用的,s2_1.__proto__ = s2_2.__proto__
  */
function Parent2() {
    this.name = 'Parent2';
    this.play = [1, 2, 3];
}
function Child2() {
    this.type = 'Child2';
}
Child2.prototype = new Parent2();
var s2_1 = new Child2();
var s2_2 = new Child2();
s2_1.play.push(4);
console.log('s2_1(push 4):', s2_1.play); // s2_1(push 4): [1, 2, 3, 4]
console.log('s2_2:', s2_2.play); // s2_1: [1, 2, 3, 4]
console.log(s2_1.__proto__ === Child2.prototype, Child2.prototype.__proto__ === Parent2.prototype); // true true
console.log(s2_1.constructor === Parent2); // trueconsole.log(s2_1.__proto__ === s2_2.__proto__); // true

3. 组合方式

缺点:

① 实例化子类时,父类执行了两次(new时、原型链上)

② 对象是由父类实例化的(使用constructor验证)

/**
  * 组合方式
  * 缺点:① 实例化子类时,父类执行了两次(new时、原型链上)
  *      ② 对象是由父类实例化的
  */
function Parent3() {
    this.name = 'Parent3';
    this.play = [1, 2, 3];
}
function Child3() {
    Parent3.call(this); // apply 改变this指向
    this.type = 'Child3';
}
Child3.prototype = new Parent3();
var s3_1 = new Child3();
var s3_2 = new Child3();
s3_1.play.push(4);
console.log('s3_1(push 4):', s3_1.play); // s3_1(push 4): [1, 2, 3, 4]
console.log('s3_2:', s3_2.play); // s3_1: [1, 2, 3]

4. 组合继承优化方式1

仍然存在的缺点:对象是由父类实例化的

/**
  * 组合继承优化方式1
  * 缺点:对象是由父类实例化的
  */
function Parent4() {
    this.name = 'Parent4';
    this.play = [1, 2, 3];
}
function Child4() {
    Parent4.call(this); // apply 改变this指向
    this.type = 'Child4';
}
Child4.prototype = Parent4.prototype;
var s4 = new Child4();
s4.play.push(4);
console.log('s4:', s4 instanceof Child4, s4 instanceof Parent4);
// 怎么区分一个对象是父类实例化的,还是子类实例化的
// 使用 constructor属性
console.log(s4.constructor == Parent4);

5. 组合继承优化方式2

注意事项:Object.create(Parent5.prototype) 创建的对象 原型对象是参数

/**
  * 组合继承优化方式2
  * Object.create(Parent5.prototype) 创建的对象 原型对象是参数
  */
function Parent5() {
    this.name = 'Parent5';
    this.play = [1, 2, 3];
}
function Child5() {
    Parent5.call(this); // apply 改变this指向
    this.type = 'Child5';
}
Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;
var s5 = new Child5();
console.log('s5:', s5 instanceof Child5); // true true
// 怎么区分一个对象是父类实例化的,还是子类实例化的
// 使用 constructor属性
console.log(s5.constructor == Parent5); // false
console.log(s5);  // Child5 {name: "Parent5", play: Array(3), type: "Child5"}