4

之前的文章地址:
2018年3月面试心得《跨域问题》
2018年3月面试心得《上下文,作用域》

想了半天,我还是对原型下手了。
近期面试的小伙伴们肯定也是面临着不少原型的问题。

“请你简单描述一下原型。”
——原型就是爸爸,原型链就是爸爸的基因。
“如何实现继承?” ——es6的extends。 “es5呢?”
——………(脸上笑嘻嘻,心里fuuuuuuuuuuuck!!!)

我想应该有不少转专业到前端来的小伙伴跟我一样,基础弱的一笔,抓了几个面试题刷了刷,完事英勇的冲了出去,昂首挺胸的被问几句,答上来了自信心爆表,但是面试官再问细一点,立马就蔫巴了~
不得不说,大厂都会抓着你基础死劲问,因为他们认为基础决定一切。(他们是不是这么认为的我也不知道,但是给我感觉就是这样……)
接下来,我们要好好的来巩固一下原型的基础啦。

首先,我们先玩个函数。

// 我先个你一个儿子的设计图
function 儿子(name, age, isGay) {
    this.name = name;
    this.age = age;
    this.isGay = true;
}

// 现在我要开始造儿子了!!!
var myson = new 儿子('小明', 50, false)

// 现在打印一下我们的myson
//{name: "小明", age: 50, isGay: true}

// 卧槽怎么回事,我儿子怎么是个gay!不行!
myson.isGay = false

// 好了这下舒坦了 嘿嘿嘿嘿

上面的这个就是构造函数,我们也可以叫他为儿子生成器,或者图纸。
我们拿着图纸去生成不同的儿子,儿子你又可以给他新的属性等等~你可以diy~
是不是很有意思。
好了我们进入正题。


普通对象和函数对象

JavaScript中万物皆对象,但对象之间也是有区别的。分为普通对象函数对象

我们大前端是不怕单身的,没对象可以new一个。
在看代码的时候,经常会看到这些东西。

var a = new Object;
var b = new funxtion() {……}

这就是被我们new出来的对象。
很明显,上面的是普通对象,下面的是函数对象。

普通对象如果加上了prototype的属性,那么就变成了原型对象。
每个函数对象创建的时候,都会自带一个prototype的属性,这个属性相当于一个指针,指向他本身的原型对象,这个原型对象里包含着自定义的方法属性

好了,让我们来实践一下,首先我们要做一只猫咪和一只狗狗。

var miao;
miao.prototype = {
    name = "大蛋蛋",
    age = 1
}
function dog(name, age){
    this.name = name,
    this.age = age
}

// 现在我们把猫狗的图纸画好了,上面是原型对象的方式,下面是函数对象,接下来我们就可以来创建一些猫猫狗狗了。

我们停一下

昨晚在我写到这里的时候,去看了一本书,上面写着其实js的继承,其实并不应该叫继承。
在其他语言中,继承是属于复制。

创造了一个爸爸,那么继承就是我复制了很多爸爸出来,每个爸爸都一样的。
当然你也可以给你创造的每个爸爸一些单独的属性,这个爸爸会做饭,那个爸爸会钓鱼。

在you don`t know js里面的第五章写着:

本来设计js语言的时候并没有想将它做的太过复杂。
通过new的函数调用并没有创造关联,这个关联只是一个**意外的副作用**。

这个观点我目前还不是太清楚对错,但是我们可以暂时先这么理解一下。

如果说其他语言的继承,就是复制的话,你创造一个爸爸图纸,可以作出不同的爸爸。

那么js的理解应该就是,你创造一个爸爸,并且生出一堆儿子,每个儿子都可以拥有爸爸给孩子的东西,例如钓鱼、做饭(爸爸的方法)。

为什么会拥有他的方法呢?

因为在儿子还没有学习这个方法的时候,我们班主任让儿子去钓鱼,那么儿子不会,就会向上查找去叫爸爸,如果爸爸有这个技能,我们就可以成功完成任务了。

现在我们来用做简单的语言来阐述一下原型和原型链。
什么是原型?那就是爸爸。
什么是原型链?那就是爸爸和儿子的关系。

不知道这么解释会不会有点牵强,不过这确实可以有助于我们去实践下一步。


现在开始真正的实践。

我们还是简单去做一个爸爸吧。

function 爸爸(name) {
    // 现在我给爸爸丢上一些方法
    // 给孩子命名
    this.name = name;
    // 爸爸愿意交给儿子可以钓上来你想要的鱼
    this.goFishing = function(fis) {
        return fis
    }
}
// 现在我们来创造儿子
var 儿子小明 = new 爸爸('小明')

儿子小明.name //"小明"
儿子小明.goFishing('章鱼') //"章鱼"

// 爸爸没有直接较
爸爸.prototype.cook = function () {
    ['米饭', '爆炒鱿鱼', '番茄蛋汤'].forEach(i => console.log(i))
}
儿子小明.cook()
// 米饭 爆炒鱿鱼 番茄蛋汤

现在我们打印一下小明儿子。
儿子里面有:{name: "小明", goFishing: ƒ}
这是爸爸直接给儿子的东西。
那么爸爸还没给儿子,儿子却可以使用的到哪里去了呢?

我们往下再看,有一个__proto__
在这里面,就能发现爸爸的cook。

所以爸爸的独门绝技丢在了prototype里面,这里面是儿子不需要学习却可以直接使用的独门绝技~

构造函数属性(constructor)

这个又是什么呢?
其实在刚刚我们打印儿子小明的时候,在cook下面,有一个constructor的东西,就是这玩意。
还是刚刚的代码,现在跟着我一起打印一下儿子小明.constructor
现在直接把爸爸打印出来了。

这个怎么理解呢……
我找到了一句话。

在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)

好了,就按照这句话这么理解的话,我们可以理解为:

所有的儿子都会有一个constructor的属性,这个属性指着可以获取爸爸独门绝技(prototype)的那个爸爸。

现在我叫他找爸爸属性……

现在我们再创建一个女儿

var 女儿小花 = new 爸爸('小花')

儿子小明.constructor === 女儿小花.constructor // ture

那我们用了找爸爸属性,所以小明和小花的爸爸是同一个爸爸~

现在我们再仔细看一下刚刚打印出来的小明。

敲黑板。

goFishing:ƒ (fis)
name:"小明"
__proto__:{
    cook:ƒ ()
    constructor:ƒ 爸爸(name)
    __proto__:Object
}

仔细看,constructor在哪里~
大声告诉我在哪里~
在__proto__里面对吧。
所以这个找爸爸的属性是从爸爸那里带过来的,是爸爸告诉你怎么找爸爸,并且没有实际教会你怎么找爸爸,是直接调用爸爸的独门绝技找的爸爸~

狂敲黑板!

现在我们再来往下科普一下其他的几个数据类型,再研究其原型。

它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)
还有ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。

我们现在先来玩一下数字。

var 数字 = 12;
// 他是否有爸爸呢?
console.log(数字.__proto__)
// 跟着我敲敲看。
var 数字1 = new Number(12)
数字 == 数字1 // true
数字 === 数字1 // false

在打印数字的proto的时候,瞧我们看到了什么!
Number!
这就是他爸爸!

最后谈一下继承的方法吧。

es6:

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

es5:

方法一:

子.prototype = new 爸爸()

新建一个爸爸作为孩子的独门绝技~
但是要想为儿子新增属性和方法,必须要在new 爸爸()这样的语句之后执行,不能放到构造器中……
来自原型对象的引用属性是所有实例共享的,创建子类实例时,无法向父类构造函数传参

方法二:

function 儿子(name){
 父亲.call(this);
 this.name = name || 'Tom';
}

但是这样的话,每个儿子都要绑定爸爸的实例函数的副本,影响性能

方法三:

儿子.prototype = Object.create(爸爸.prototype)
创造一个新爸爸的独门绝技来当儿子的独门绝技~

其实方法有很多,我就不一一举例了。

ECMAScript 继承机制实现

如果对文章有什么间接,欢迎悄悄告诉我~ =333333=
么么哒~
上面我还写了数字和数字1,猜猜看我下一章写的是啥~~~


darklinda588
141 声望6 粉丝

引用和评论

0 条评论