原型与继承

204 阅读3分钟

创建对象的几种方法

  1. 对象字面量
var obj1 = {
    name:'luoyi'
}
var obj2 = new Object({
    name:'louyi3'
}) //也可以归类为构造函数形式

  1. 构造函数形式
var Foo = function(){
    this.name = 'luoyi'
}
var foo = new Foo()

  1. Object.create()方法
var obj = {
    name:'luoyi'
}
var obj2 = Object.create(obj)

原型与原型链

var Foo = function(name){
    this.name = name
}
var foo = new Foo('luoyi')
  1. 构造函数:构造函数可以使用new运算符来生成一个实例
  2. 实例:通过构造函数new出来的对象
  3. prototype: 函数都有prototype属性
  4. 原型对象:函数的prototype属性就是原型对象
  5. constructor: 原型对象上有一个constructor属性指向构造函数
  6. 实例的__proto__属性指向函数的prototype属性(原型对象)

instanceof的原理

instanceof 是通过原型链判断的,A instanceof B, 在A的原型链中层层查找,是否有原型等于B.prototype,如果一直找到A的原型链的顶端(null;即Object.proptotype.proto),仍然不等于B.prototype,那么返回false,否则返回true.

new一个对象的过程

  1. 创建一个新对象。
  2. 这个新对象继承自构造函数的prototype,即会被执行原型连接。
  3. 构造函数被执行,执行的时候参数被传入,this被指定为新实例
  4. 如果构造函数返回了一个对象,那么这个对象就会取代整个new出来的结果;如果构造函数没有返回其他对象,那么new出来的结果为步骤1中创建的对象。

自定new的过程

function new1 (func){
  var o = Object.create(func.prototype)
  var [func,...args] = [...arguments]
  var k = func.call(o,...args)
  if(typeof k === 'object'){
    return k
  }else{
    return o
  }
}

继承

类的声明

// ES5的实现方式
funciton Animal1(){
    this.name = 'name'
}
// ES6中的实现方式
class Animal2{
    constructor(){
      this.name = 'name'
    }
}
typeof Animal2 // 'function' 类的数据类型就是函数,类本身就指向构造函数
Animal2 === Animal2.prototype.constructor // true

实例化类的对象

ES5的实例化方式与ES6的实例化方式相同

var cat = new Animal()

类的继承

继承的本质是原型链

  1. 借助构造函数实现继承
function Parent (){
  this.name = 'parent'
}
function Child (){
  Parent.call(this) 
  this.type = 'child'
}
Parent.prototype.say = function(){
  console.log('say hi')
}
var child = new Child()
child.say() //没有这个方法

  • 缺点:Parent原型链上的方法是没有办法被继承的,没有实现真正的继承。
  1. 借助原型链实现继承
function Parent2 (){
    this.name = 'parent2'
}
function Child2(){
    this.type = 'child2'
}
Child2.prototype = new Parent2()
var child2 = new Child2()

  • 缺点1:Child2的所有实例对象共享__proto__:c2.__proto__ === c22.__proto__
function Parent2 (){
    this.name = 'parent2';
    this.play = [1,2,3]
}
function Child2(){
    this.type = 'child2'
}
Child2.prototype = new Parent2()
var c2 = new Child2()
var c22 = new Child2()
c2.play.push(4)
c22.play // [1,2,3,4]
  • 缺点2:child2.constructor指向的Parent2

  1. 组合继承:构造函数继承和原型链继承的组合
function Parent3(){
    this.name = 'parent3'
    this.play = [1,2,3]
}
function Child3(){
    Parent3.call(this)
    this.type = 'child3'
}
Child3.prototype = new Parent3();
var s = new Child3()
var s1 = new Child3()
s.play.push(4)
s.play() //[1,2,3,4]
s1.play() // [1,2,3]
  • 缺点:父类的构造函数执行了两次

组合继承的优化方式1

function Parent3(){
    this.name = 'parent3'
    this.play = [1,2,3]
}
function Child3(){
    Parent3.call(this)
    this.type = 'child3'
}
//Child3.prototype = new Parent3();
Child3.prototype = Parent3.prototype;
var s = new Child3()
var s1 = new Child3()
s.play.push(4)
s.play() //[1,2,3,4]
s1.play() // [1,2,3]
  • 缺点:constructor指向的是父类的构造函数

组合集成的优化方式2

function Parent3(){
    this.name = 'parent3'
    this.play = [1,2,3]
}
function Child3(){
    Parent3.call(this)
    this.type = 'child3'
}
//Child3.prototype = new Parent3();
//Child3.prototype = Parent3.prototype;
Child3.prototype = Object.create(Parent3.prototype) 
Child3.prototype.constructor = Child3

Zepto中是如何使用原型链

写一个实际应用中原型链继承的例子:封装一个DOM查询的例子

function Elem(id){
  this.elem = document.getElementById(id)
}
Elem.prototype.html = function(val){
  var elem = this.elem
  if(val){
    elem.innerHTML = val
  }else{
    return elem.innerHTML
  }
}
Elem.prototype.on = function(type,fn){
  var elem = this.elem
  elem.addEventListener(type,fn)
}
var div1 = new Elem('div1')