前言

你好! 欢迎阅读本篇文章,今天我将要用一篇文章带你深入理解JavaScript中的原型和原型链,让我们一起揭开原型神秘的面试,一起窥探其中的奥妙。

一切皆是对象

看到这个标题你可能就有你可能就有疑问了,怎么这篇文章一开始就要胡扯了,然后准备要关闭这篇这篇文章。别急、客官,接下来就让我们仔细掰扯掰扯这句话

“一切皆是对象”是没错,关键在于你如何理解这个“对象”这个概念。这句话的重点也在于此。

在理解对象之前,我们先引用一个我们的老朋友 typeof ,相信很多前端的同学都应该知道这个JavaScript的常用运算符,如果你不知道,请仔细阅读下面文字。

我们都知道 typeof 可以判断一个变量的类型,返回值共有一下几种( undefined、number、string、boolean、function、object、Symbol )其中 Symbol 是 ES6 引入的新类型。

function test() { var x; console.log(typeof x) //undefined console.log(typeof 100) //number console.log(typeof 'typeof') //string console.log(typeof true) //boolean console.log(typeof [1,2,3,4]) //object console.log(typeof {a:1,b:2}) //object console.log(typeof null) //object null在这里为空对象(注意) console.log(typeof new Number(100)) //object console.log(typeof function(){}) //function }

上面代码列出的是 typeof 返回的几种值,其中代码前四种是值类型,余下的几种情况都是引用类型,他们也都是对象。关于引用类型和值类型的区别请看我们另一篇文章值类型和引用类型.

对象是若干属性的集合,对象里面的一切都是属性,都是以键值对的形式出现,其中方法也是一种特殊的属性。属性的值也能是一个对象(禁止套娃qAq)对象可以用instanceof判断,记住一切(应用类型)都是对象,对象是属性的集合,一切对象都是通过函数来创建的

原型知多少

说到原型我们就不得不说 prototype 我们知道每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。我放了两张图片帮你理解(qwq)

js原型方法(一篇文章读懂JS原型和原型链)(1)

上图SuperType是函数,他的原型就是SuperType Prototype,原型的constructor属性指向它的构造函数SuperType

js原型方法(一篇文章读懂JS原型和原型链)(2)

上图是对象的Object的以及它的原型,我们可以看到原型上有很多我们常用的属性和方法,应为一切对象的原型都直接或间接的指向了它,因此可以使用上面的方法。

上面说到每一个函数都有自己的prototype,也就是原型,那么通过 new 生成的函数实例呢?这里就不禁要疑问了,其实每个new出来的实例,以及每一个对象都有一个__proto__,即隐式原型。

js原型方法(一篇文章读懂JS原型和原型链)(3)

上面图片显示了对象obj的__proto__属性,下面我再放一张Object的prototype

js原型方法(一篇文章读懂JS原型和原型链)(4)

从上面的截图看出,obj.proto__和Object.prototype是一样的我们用一张图表示。

js原型方法(一篇文章读懂JS原型和原型链)(5)

从图中我们得知每个对象都有都有一个__proto,并且指向该对象函数的prototype。

唯一的特例Object.prototype的__proto__指向的是null

还有就是,函数也是对象,函数也有一个__proto__,指向Function.prototype,函数是由Function创建出来的,而Function又被自身创建。

var foo = new Function("x","y","return x y")

就是这样,建议不要这样创建函数

原型链和继承

下面用一张详细的图,请仔细阅读,我们根据这张图讲解原型链

js原型方法(一篇文章读懂JS原型和原型链)(6)

要说起原型链,我们不得不用到一个工具instanceof,对于引用类型,我们想要知道引用类型的具体是数组还是对象,就要用到这个工具。如下所示

function Foo(){} var f1 = new Foo() console.log(foo instanceof Foo) //true console.log(foo instanceof Object) //true

结合上图我们我们来看看,instanceof运算符的规则就是,实例foo沿着__proto__这条线,同时Foo沿着prototype这条线来找,如果这两条线找到了同一个对象,那么就返回true,否则就返回false, instanceof表示一种继承关系,或者原型链的结构

JavaScript是通过原型链的方式来实现继承的,又被称作为原型链对象继承,对于一个对象,它可以访问它原型对象的属性。同理,原型对象也是一个属性,它也有自己的原型,因此也可以继承它原型对象原型的属性。

访问一个对象的属性时,现在基本的对象内找,如果找不到,就沿着__proto__这条链向上找。

几种常见的继承方式

组合继承

组合继承(combination inheritance),有时候也叫做伪经典继承,指的是将原型链和借用构造函数的

技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方

法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数

复用,又能够保证每个实例都有它自己的属性。下面来看一个例子

function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ //继承属性 SuperType.call(this, name); this.age = age; } //继承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27

ES6中继承

ES6是通过class实现了类,通过extends实现了标准化继承,然而,class、extends本质上还是语法糖,通过bable可以将其转译为ES5代码执行。

ES6中的class、extends使用的是ES5中寄生组合继承,并对寄生组合继承作出了一下优化,如下所示

ES6中继承使用Object.create()以SuperClass的原型对象创建了一个新的原型对象,即SubClass.prototype.proto = SuperClass.prototype。 同时使其constructor属性指向SubClass。

ES5的继承是子类的原型对象__proto__属性指向父类的原型对象,而ES6中继承是子类__proto__指向父类。

写在最后

以上就是本人对原型和原型链的整理和记录,因为资料来源比较多,在整理和书写是难免出现错误,如果你看到这里,发现原文有错误的地方,或者有什么想要分享的,也欢迎在评论区留言,一起学习,加油,奥利给

js原型方法(一篇文章读懂JS原型和原型链)(7)

,