js继承

伪类

首先要记住一件事,js中没有类的概念。

function Animal(name){
  this.name = name
}
Animal.prototype.say = function(){
  console.log("I'm", this.name)
}

function Bird(name, type){
  Animal.call(this, name)
  this.type = type
}
Bird.prototype = Object.create(Animal.prototype)

上面的代码中,我们创建了两个“类”,Bird继承了Animal,我们创建的bird都会继承到animal中的say方法。
当一个函数对象被创建时,Function构造器产生的函数对象会运行一些代码,类似

this.prototype = {constructor: this}

所有的函数对象在被创建时都会得到一个prototype属性。
但是这种方法不是纯粹的原型继承。在构造对象的时候,我们不能忘记使用new关键字,不然this将无法指向我们新创建的对象,造成全局变量污染。

原型继承

正确的做法是,不要在写构造函数了(不要使用伪类),直接写需要的对象。比如当我们需要一个animal的时候,就直接写一个animal出来,而当你需要一个bird的时候,先用Object.create创建一个原型链到animal的原型上的对象,然后再添加bird需要的属性。当你需要一个鸭子的时候,先用Object.create创建一个原型链到bird的原型上的对象,然后再添加duck需要的属性。

var myAnimal = {
  name : 'myAnimal',
  get_name: function(){
    console.log("I'm",this.name)
  }
}

var myBird = Object.create(myAnimal)
myBird.name = "myBird"
myBird.type = "Duck"
myBird.get_name()

var mytanglaoya = Object.create(myBird)
mytanglaoya.name = "唐老鸭"
mytanglaoya.get_name()

为啥不直接连过去

有的同学可能会问,上面的代码好像很麻烦哎,为什么不能直接原型连过去,就像下面这样

var foo = {bar:"bar1"}
foo.prototype = Object.prototype
foo.prototype.getBar = function(){
  return this.bar
}
var baz = {bar:"bar2"}
baz.prototype = foo.prototype
console.log(baz.getBar())   //bar2

看上去一切都很美好,但是有一天,baz不爽了,它觉得自己应该和其他的foo区分开,于是,它修改了自己的原型,在每次getBar的时候先打上自己的名号

baz.prototype.getBar = function(){
  return "baz: " + this.bar
}
console.log(foo.getBar())   //baz: bar1

然而没有想到的是,baz的原型和foo是同一个,他在修改自己原型的时候,其实修改了foo的原型,于是...
我们可以用Object.getPrototypeOf来查看对象的原型

console.log(Object.getPrototypeOf(foo) === Object.getPrototypeOf(baz))  //true
console.log(baz.__proto__ === foo)   //true

Object.create

其实Object.create的原理非常简单,我们找个“中介”F,并把这个构造函数的原型指向待继承的原型,当调用new F()的时候,我们创建的新对象的原型和传入的prototype被隔离开了。

Object.create = function(prototype){
  var F = function(){}
  F.prototype = prototype
  var result = new F()
  return result;
}

上面的代码中,首先创建了一个构造函数F,F将被用来构造一个对象,但是在这之前,我们要把他的原型连接到待继承对象的原型上,然后创建对象并返回。
事实上,

function Foo(){}
var foo = new Foo()

等价于

var foo = Object.create(Foo.prototype);

下面我们来测试一下

a = {p1:"p1"}
b = Object.create(a)
console.log(Object.getPrototypeOf(a) === Object.getPrototypeOf(b))  //false

works great! 现在原型得到了很好的隔离

functional

现在我们学会了伪类继承和原型继承。在伪类继承中,我们首先设计“类”,其实就是构造函数,并且将构造函数的原型链加以连接。但是伪类继承需要使用new关键字,而且这种设计有点不伦不类。更好的方式是使用纯粹的原型继承,我们不在通过构造函数来实现继承,而是通过Object.create函数,把被继承的对象的prototype复制到后代对象的原型上,然后在做差异化。
但是这两种方法都没有解决一个问题:私有变量。
在面向对象编程中,有一个很重要的特点就是封装。我们需要把一些特定的属性变成私有属性,只有通过特定的方法(接口)才可以访问这些属性。这里我们需要用到的就是所谓的functional。

var animal = function(spec){
  var that = {};
  that.get_name = function(){
    return spec.name
  }
  that.says = function(){
    return spec.saying || '';
  };
  return that;
}
var myAnimal = animal({name:"Herb"})
console.log(myAnimal.name) //undefined
console.log(myAnimal.get_name()) //Herb

我们的代码中,animal是一个标准的函数,我们将一个对象传入animal函数作为animal的变量,并返回一个拥有两个函数的对象。我们无法直接通过myAnimal来访问name,因为myAnimal对象只有两个方法,但是,我们可以通过myAnimal的两个方法来访问name,这样我们就得到了对象的私有环境。

当我们想要实现cat并让其继承animal的时候,一切都非常简单,我们只需要写一个cat函数,并在其中执行animal函数就可以了。一切都是最纯粹的函数而已!

var cat = function(spec){
  spec.saying = spec.saying || 'meow'
  var that = animal(spec)
  that.get_name = function(){
    return spec.name + 'cat'
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • 原文链接 js的继承有6种方式,大致总结一下它们各自的优缺点,以及它们之间的关系。 1.原型链 js的继承机制不同...
    空_城__阅读 788评论 0 11
  • 开山例子 需求:学生交学费,中学生打8折;小学生打5折,然后通过打印的方法,显示学生的名字、年龄记应缴学费。 我们...
    jayafs阅读 565评论 0 50
  • 对于js继承一直都是半懂半不懂的状态,感觉需要整理一下: 参考自:JavaScript原型继承工作原理JavaSc...
    博客专用马甲阅读 4,195评论 2 20
  • 背景音乐:Leonard Cohen《 In My Secret Life》 下半年,碰到各种各样的关系危机。 我...
    樱桃猫腻阅读 502评论 0 1
  • 从一开始参加工作起就跟印度人打交道,彼此之间在工作中的套路算得上是提头知尾了。即便如此,但很多时候还是表示不能理解...
    唯生物阅读 961评论 0 0