聊聊原型 Prototype | 技术创作101训练营

2024-03-16 16:20:19 浏览数 (2)

原型和原型链,一直是 JavaScript 中的重要概念,也是面试官必问的知识点,或许有的同学认为,自己虽然没有很深入了解过原型和原型链,但并不影响自己日常的开发工作,其实在我们日常开发工作中,很多的工具和框架已经做到了开箱即用的程度,不需要我们了解其原理就立即上手并投入生产当中,但这些工作换个人其实也能做,我们需要做的是,把自己修炼成独一无二、无可替代的那一个,这区别就在于“懂”和“用”之间的努力了。

万物皆对象

在 JavaScript 中,万物皆对象,记住这个概念,会对你理解原型和原型链有很大帮助,原型的定义在这里就不再复述,大家只要记住两点:

a. 每个函数 function 都有一个 prototype

b. 每个对象都有一个 __proto__,指向创建该对象的函数的 prototype

访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着 __proto__ 这条链向上找,这就是原型链,而 Instanceof 表示的就是一种继承关系,或是原型链的结构

A instanceof B 判断规则

沿着 A 的 __proto__ 这条线来找,同时沿着 B 的 prototype 这条线来找,如果两条线来找,如果两条线能找到统一引用,即同一个对象,那么就返回 true,如果找到终点还未重合,则返回 false

接下来我们来看一个完整的原型链图

其中有些难理解的点,这里做一下简单分析:

a. 所有的引用类型都具有对象属性,Function 是一个对象,所以 Function instanceof Object === true

b. Object 自身是一个构造函数,函数都是继承自 Function,所以 Object instanceof Function === true

c. Function 是一个函数,函数是一种对象,也有 __proto__ 属性,既然是函数,那么它一定是被 Function 创建,所以 Function.__proto__ === Function.prototype

d. 对象的 __proto__ 指向的是创建它的函数的 prototype,所以 Object.__proto__ === Function.prototype

举个栗子

原型链在日常工作中用的也是非常多的,下面来实现一个简单的 zepto

代码语言:javascript复制
function Elem(id) {
  this.elem = document.getElementById(id);
}
Elem.prototype.html = function(val){
  var elem = this.elem
  if(val){
      elem.innerHTML = val;
      return this; // 链式操作
  }else {
      return elem.innerHTML;
  }
}
Elem.prototype.on = function(type, fn){
  var elem = this.elem;
  elem.addEventListener(type, fn)
}

既然说到了 zepto,那就来稍微拓展下 zepto 的源码,其实也很简单,也就三个部分——入口函数、构造函数、构造函数的原型,具体代码如下所示:

代码语言:javascript复制
(function(window){
  var zepto = {}

  function Z(dom, selector) {
    var i, len = dom ? dom.length : 0
    for(i = 0; i < len; i  ) this[i] = dom[i]
    this.length = len
    this.selector = selector || ''
  }
  
  zepto.Z = function(dom, selector) {
    return new Z(dom, selector)
  }

  zepto.init = function(selector) {
    var slice = Array.prototype.slice
    var dom = slice.call(document.querySelectorAll(selector))
    return zepto.Z(dom, selector)
  }

  // 即使用 zepto 时候的 $
  var $ = function(selector) {
    return zepto.init(selector)
  }

  Window.$ = $

  $.fn = {
    css: function(ley, value) {},
    html: function(value) {}
  }

  Z.prototype = $.fn
})(window)

最后来谈一下 Class,其本质还是语法糖,实现方式依旧是 prototype,但在语法上,Class 更贴近面向对象的写法,实现继承更加易读、易理解

代码语言:javascript复制
function Animal() {
  this.eat = function() {
    console.log('this is animal')
  }
}
  
function Dog() {
  this.bark = function() {
    console.log('this is dog')
  }
}

Dog.prototype = new Animal()
var hhh = new Dog()
hhh.eat()
hhh.bark()

class Animal {
  costructor(name) {
    this.name = name
  }
  eat() {
    console.log(`this is ${this.name}`)
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name) // 必须写上!!!让Animal也有name参数
    this.name = name
  }
  bark() {
    console.log(`${this.name} bark`)
  }
}

const hhh = new Dog('哈士奇')
hhh.eat()
hhh.bark()

参考文章

继承与原型链 - JavaScript | MDN

Class的继承 - ECMAScript6 入门 | 阮一峰

0 人点赞