原型和原型链,一直是 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 入门 | 阮一峰