读书笔记--对象、实例、原型、继承

创建对象的设计模式

  • 工厂模式
    • 抽象了创建具体对象的过程,用函数封装以特定接口创建对象的细节
    • 解决了创建多个相似对象的问题,没有解决对象识别的问题
function createPerson(name,age){
  var obj = new Object();
  obj.name = name;
  obj.age = age;
  obj.sayName = function(){
    alert(this.name)
  }

return obj;
}

var person = createPerson('aa',12);
  • 构造函数模式
    • 构造函数可以用来创建特定类型的对象,可以用instanceof检测类型
    • 使用new操作符会经历以下四个步骤
      • 创建一个新对象
      • 将构造函数的作用域赋给新对象(因此this指向了这个新对象)
      • 执行构造函数中的代码(为这个新对象添加属性)
      • 返回新对象
    • 缺点:
      • 每个方法都要在每个实例上重新创建一遍
      • 每个方法(函数)就是一个对象,不同实例上的同名函数是不相等的
  • 将函数抽离到外部使多个对象共共享全局作用域的函数虽然可以解决上面问题,但让this.sayName = sayName全局方法会破坏了对象的封装性
function Person(name,age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    alert(this.name)
  }
}

var person = new Person('aa',12);

person instanceof Person => true

原型模式

  • prototype
  • 只要创建了一个新函数,就会为函数创建一个prototype属性,这个属性指向函数的原型对象
  • 包含了特定类型的所有对象实例共享的方法和属性
  • prototype是通过调用构造函数而创建的那个对象实例的原型对象
  • 默认情况下所有原型对象会获得一个constructor(构造函数属性),这个属性指向prototype属性所在函数的指针
  • 当调用构造函数创建一个新实例时,内部将包含一个指针,指向原型对象,连接实例与原型对象
function Person(name,age){
 
}
Person.prototype.name = name;
Person.prototype.age = age;
Person.prototype.sayName = function(){
    alert(this.name)
  }
var person = new Person('aa',12);

person instanceof Person => true
  • 判断原型对象

    • Person.prototype.isPropertyOf(person) => true
    • Object.getPropertyOf(person) == Person.prototype => true (es5)
  • 多个对象实例共享原型所保存的属性和方法的基本原理

    • 每当代码读取某个对象的某个属性时,搜索从对象实例本身开始,如果在实例中找到具有给定名字的属性则返回该属性的值
    • 如果没有找到则继续搜索指针指向的原型对象,如果在原型对象中找到给定名字的属性,则返回这个值
  • 虽然可以通过对象实例访问保存在原型实例中的值,但是不能通过对象实例重写原型中的值,如果在实例中添加一个与原型实例中的一个属性同名,访问时实例属性会屏蔽原型中这个属性的值,只能访问到这个实例上属性的值。可以通过delete操作符删除实例属性,重新访问原型中的属性

  • 使用hasOwnProperty()可以检测一个属性是存在实例中还是原型中,只有存在实例中才会返回true;person.hasOwnProperty('name')

  • 'name' in person 无论name是存在实例中还是原型对象中都会返回true

  //判断对象原型上是否有这个属性
  function hasPrototypeProperty(obj, attr){
    return (attr in obj) && !obj.hasOwnProperty(attr);
  }
  • 在使用for-in 循环时,返回的是能够通过对象访问的,可枚举的属性,包括了实例上的属性和原型上的属性。原型上不可枚举的属性,但是在实例中定义了也可以获取得到,如在实例中定义toString

  • Object.keys() (es5)

    • 取得对象上所有可枚举的实例属性,返回一个包含所有可枚举的字符串数组
    • 也可以传入一个原型对象,Object.keys(Person.prototype),但不会沿着原型链往上寻找,只返回当前prototype下的属性
  • Object.getOwnPropertyNames()(ie9+)

    • 枚举所有实例属性,不管是否可枚举

原型的动态性

  • 在原型中查找值的过程是一次搜索,对原型 对象所做的任何修改都能立即从实例上反映出来--即使是先创建了实例后修改原型也照样如此
  var friend = new Person();
  Person.prototype.sayHi = function(){
      
    alert('hi')
  }
  friend.sayHi() //hi
  • 实例和原型之间松散的连接关系,可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来
  • 但是重写整个原型对象就不一样了,调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型的联系
  • 实例中的指针仅指向原型对象,而不指向构造函数
  var friend = new Person();
  Person.prototype = {
    sayHi:function(){
      alert('hi')
    }
  }
  friend.sayHi() //error
Paste_Image.png
  • 原型模式的缺点
    • 省略了为构造函数传递初始化参数,所有实例在默认情况下都将取得相同的值
  • 当使用原型属性时会只要在一个实例上修改都会影响到所有的实例,例如在一个实例上修改数组

组合使用构造函数和原型模式

  • 构造函数定义实例属性,原型模式定义方法和属性

动态原型模式

  • 通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型
 function Person(name,age){
  this.name = name;
  this.age = age;
  **if( typeof this.sayName != 'funcction'){
     Person.prototype.sayName = function(){
      alert('hi')
    }
  }**
}
  • 只有初次调用构造函数时才会执行将函数添加到原型中

寄生构造函数模式

  • 类似于工厂模式,封装创建对象的代码,然后返回新创建对象,return语句可以重写调用构造函数时返回的值
  function Person(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
      alert('hi');
    }
  return o;
  }
  • 使用场景:特殊情况下用来为对象创造构造函数。

  • 假设创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数

    function SpecialArray(){
      var arr = new Array();
      arr.push.apply(values,arguments);
      arr.toPipedString = function(){
      return this.join('|');  
    }
    return arr;
    }
    

var colors = new SpecialArray('green','red');
colors.toPipedString()

- 缺点:返回的对象和构造函数或者与构造函数的原型属性之间没有什么关系,不能使用instanceof来确定对象类型

### 稳妥构造函数模式
- 特点
  - 新创建对象的实例方法不引用this
  - 不使用new操作符调用构造函数
  - 除了返回对象上的属性和方法,没有其他办法访问到构造函数内的数据

  ```js
  function Person(name,age){
    var o = new Object();
    //这里可以定义私有数据
    o.sayName = function(){
      alert(name)
  }
  return o;
  }
var person = Person('green',12);
person.sayName() //只能通过sayName()方法去访问name的值

继承

原型链继承
  • js以原型链作为实现继承的主要方法
  • 基本思想是利用原型链让一个引用类型继承另一个引用类型的属性和方法
  • 构造函数、原型、实例的关系
    • 每个构造函数都有一个原型对象

    • 原型对象都有一个指向构造函数的方法

    • 实例都包含一个指向原型对象的一个指针

function SuoperType(){
  this.property = true; 
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
}
function SubType(){
  this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubType = function(){
  return this.subprototype;
}
var instance = new SubType()
instance.getSuperValue();//true
833879510766076138.jpg
  • SubType不仅具有作为一个SuperType的实例所拥有的全部属性和方法,而且拥有一个指向SuperType原型的指针

  • instance指向SubType原型,SubType原型又指向SuperType的原型

  • 确定实例与原型的关系

  • instanceof操作符 只要用这个操作符来测试实例与原型中出现过的构造函数,结果就会返回true

  • isPropertyOf() 只要是原型链中出现的原型,都可以说是该原型链所派生的实例的原型,都会返回true

  • 缺点:

    • 原型属性会被所有实例共享,如果在父构造函数中有一个this.colors=[]的数组,子构造函数继承后的实例可以修改这个存在于子构造函数原型对象上的原型属性
    • 在创建子类型的实例时,不能向超类型的构造函数传递参数。实际上是没有办法在不影响所有实例的情况下给超类型的构造函数传递参数
借用构造函数
  • 在子类型构造函数的内部调用超类型构造函数,通过call,apply 改变对象的this指向
function SuoperType(name){
  this.colors = ['red']; 
  this.name = name;
}

function SubType(name){
  SuperType.call(this,name);
}
SubType.prototype = new SuperType();
var instance = new SubType('1')
instance.colors.push('green') => ['red','green']

var instance2 = new SubType('2')
instance2.colors.push('black') => ['red','black']
组合继承
  • 将原型链和借用构造函数的技术组合到一起
  • 使用原型链实现对原型属性和方法的继承
  • 使用借用构造函数实现对实例属性的继承
function SuperType(name){
  this.colors = ['red']; 
  this.name = name;
}
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.sayName = function(){
  alert(this.name)
}
var instance = new SubType('1',12)
instance.sayName() => 1
instance.colors => ['red']
var instance2 = new SubType('2',21)
instance2.sayName() => 2
instance2.colors => ['red']
原型式继承
  • 基于一个对象上,这个对象相当于作为原型,再根据需求对得到的对象加以修改
  • 在没有必要兴师动众创建构造函数,而只想让一个对象与另一个对象保持类似的情况下使用
  function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
  }
  var person = {
    name:'n',
    friends:['a','b']
  }
  var anotherPerson = object(person);
  anotherPerson.name = 'y';  =>y
  anotherPerson.colors.push('c');  => ['a','b','c']
  var person = object(person);
 person.name = 'yy';  =>yy
person.color; =>['a','b','c']
  • Object.create() es5新增规范原型式继承

 var person = {
   name:'n',
   friends:['a','b']
 }
 var anotherPerson = Object.create(person);
 anotherPerson.name = 'y';  =>y
 anotherPerson.colors.push('c');  => ['a','b','c']
 var person =  Object.create(person);
person.name = 'yy';  =>yy
person.color; =>['a','b','c']
寄生式继承
  • 创建一个仅用于封装继承过程的函数,在函数内部以某种方式增强对象
  function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
  }
  function createAnother(original){
    var clone = object(original);
    clone.sayHi = function(){
      alert('hi')
    }
  return clone;
  }
  var person = {
    name:'n',
    friends:['a','b']
  }
  var anotherPerson = createAnother(person);
  anotherPerson.sayHi(); =>hi
寄生组合式继承
  • 组合继承最大的问题是无论什么情况下都会调用两次超类型构造函数
    • 一次是创建子类型原型时,一次是子函数的内部构造函数
  • 寄生组合继承通过借用构造函数来继承属性,通过原型链混成形式来继承方法。
  • 思路是不必为了指定的子类型原型而调用超类型的构造函数。
  • 使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型
    function inheritPrototype(subType,superType){
      var prototype = object(superType);
      prototype.constructor = subType;
      subType.prototype = prototype;
  }

function SuperType(name){
  this.colors = ['red']; 
  this.name = name;
}
SuperType.prototype.sayName = function(){
  alert(this.name)
}
function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
}
SubType.prototype =inheritPrototype(subType,superType) ;
SubType.prototype.sayName = function(){
  alert(this.name)
}

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

推荐阅读更多精彩内容