以前的文章地址:
2018年3月面试心得《跨域问题》
2018年3月面试心得《上下文,做用域》前端
想了半天,我仍是对原型下手了。
近期面试的小伙伴们确定也是面临着很多原型的问题。es6
“请你简单描述一下原型。”
——原型就是爸爸,原型链就是爸爸的基因。
“如何实现继承?” ——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~
是否是颇有意思。
好了咱们进入正题。segmentfault
JavaScript中万物皆对象,但对象之间也是有区别的。分为普通对象和函数对象。跨域
咱们大前端是不怕单身的,没对象能够new一个。
在看代码的时候,常常会看到这些东西。函数
var a = new Object; var b = new funxtion() {……}
这就是被咱们new出来的对象。
很明显,上面的是普通对象,下面的是函数对象。性能
普通对象若是加上了prototype的属性,那么就变成了原型对象。
每一个函数对象建立的时候,都会自带一个prototype的属性,这个属性至关于一个指针,指向他自己的原型对象,这个原型对象里包含着自定义的方法属性。学习
好了,让咱们来实践一下,首先咱们要作一只猫咪和一只狗狗。this
var miao; miao.prototype = { name = "大蛋蛋", age = 1 } function dog(name, age){ this.name = name, this.age = age } // 如今咱们把猫狗的图纸画好了,上面是原型对象的方式,下面是函数对象,接下来咱们就能够来建立一些猫猫狗狗了。
昨晚在我写到这里的时候,去看了一本书,上面写着其实js的继承,其实并不该该叫继承。
在其余语言中,继承是属于复制。es5
我创造了一个爸爸,那么继承就是我复制了不少爸爸出来,每一个爸爸都同样的。
固然你也能够给你创造的每一个爸爸一些单独的属性,这个爸爸会作饭,那个爸爸会钓鱼。
在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里面,这里面是儿子不须要学习却能够直接使用的独门绝技~
这个又是什么呢?
其实在刚刚咱们打印儿子小明的时候,在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) 创造一个新爸爸的独门绝技来当儿子的独门绝技~其实方法有不少,我就不一一举例了。
若是对文章有什么间接,欢迎悄悄告诉我~ =333333=
么么哒~上面我还写了数字和数字1,猜猜看我下一章写的是啥~~~