js继承——从建立对象开始

从建立对象开始安全

建立对象的简单方法就是:使用Object构造函数或者对象字面量。但这两个方法有个缺点:建立相同对象,要编写大量重复代码。app

为了不这个问题——>工厂模式:用函数封装以特定接口建立对象函数

function createPerson(name, age){            
  var o = new Object();
  o.name = name;
  o.age = age;
  o.sayName = function(){
    return this.name;
  }
  return o;
}
var p1 = createPerson("Tom", 23);
var p2 = createPerson("Joe", 20)
p1 instanceof createPerson    //false 
 

工厂模式:优势: 解决了建立多个类似对象问题   缺点:没有解决对象识别问题(不知道对象的类型)this

为了解决对象识别问题——>构造函数模式:建立特定类型的对象 spa

function Person(name, age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
      return this.name;
  }
}
var p1 = new Person("Tom", 23);
var p2 = new Person("Joe", 20);
p1 instanceof Person    //true   检测对象类型
p1.constructor === Person  //true 识别对象类型


p1.sayName === p2.sayName //false

构造函数模式:Person()与createPerson()   不一样之处:没有显示建立对象、将属性方法值赋值给this、没有return 语句,还有就是函数名一个字母大写。prototype

构造函数也是函数,那么函数用new执行会发生什么呢(重点)设计

          1.建立一个空对象。code

          2.将构造函数的做用域赋值给新对象(this指向该对象, 同时还继承了该函数的原型)。对象

          3.执行构造函数中的代码(添加属性和方法)blog

          4.隐式的返回this(返回该对象, 若是没有显示的返回其余对象的话)

上面说了构造函数也是函数,只是使用了new 调用,若是不使用new就和普通函数同样

var wp = Person("Marry", 18);
window.sayName();  //"Marry"
wp.sayName();  //TypeError: Cannot read property 'sayName' of undefined
sayName()    //"Marry"

构造函数解决了对象识别问题,可是缺点:正如前面(p1.sayName === p2.sayName //false )会致使不一样做用域链和标识符解析

//构造函数中的
this.sayName = function(){
      return this.name;
  }
//等价于
this.sayName = new Function("return this.name;")

为了解决这个问题,咱们能够把sayName函数放外面做为全局函数,但这方法缺点颇多,很不合适。因而...

出现了——>原型模式:函数有个prototype属性,指向一个对象,该对象能够包含由特定类型的全部实例共享的属性和方法,简称之prototype是p1,p2的原型对象。这个原型对象可让全部实例共享它包含的方法和属性。

function Person(){}
Person.prototype.name = "Tom";
Person.prototype.age = 20;
Person.prototype.friends = ["A", "B", "C"];
Person.prototype.sayName = function(){
  return this.name;
}
var p1 = new Person();
var p2 = new Person();
p1.sayName === p2.sayName;   //true

p1.name = "other";
p2.friends.push("D");
p1.sayName(); // "other" ---来自实例
p2.sayName(); // "Tom"; ---来自原型

p1.friends;  //["A", "B", "C", "D"]  ----引用类型
p2.friends;  //["A", "B", "C", "D"]

Person.prototype.isPrototypeOf(p1)   //true
Object.getPrototypeOf(p1) === Person.prototype  //true

//in 经过对象能访问给定属性就返回true,无论实例仍是原型
"name" in p1;  //true
"name" in p2;  //true

//
hasOwnProperty() 只返回实例中的属性 p1.hasOwnProperty("name"); //true p2.hasOwnProperty("name"); //false //Object.keys() 返回对象上全部可枚举实例属性 Object.keys(Person.prototype); //["name", "age", "friends", "sayName"] Object.keys(p1); //["name"]
//
Object.getOwnPropertyNames() 返回全部实例属性,不管是否可枚举 Object.getOwnPropertyNames(Person.prototype) //["constructor", "name", "age", "friends", "sayName"] Object.getOwnPropertyNames(p1) //["name"]

说明:实例,构造函数,原型的关系:实例的[[Prototype]](__proto__)只指向原型、构造函数的prototype属性指向原型、原型的constructor指向构造函数。

         in操做符:在<= IE8中 in不会枚举[[Enumerate]]为false的同名属性 ;在safari3中 in会枚举被隐藏的属性。 因此in慎用。

        其中上述代码

Person.prototype.name = "Tom";
Person.prototype.age = 20;
Person.prototype.friends = ["A", "B", "C"];
Person.prototype.sayName = function(){
  return this.name;
}
//可写成:            ---至关于重写原型
Person.prototype = {
  constructor: Person,
  name = "Tom",
  age = 20,
  friends = ["A", "B", "C"],
  sayName = function(){
    return this.name;
  }
}

//支持ES5中,可去掉 上句 constructor: Person,添上以下代码:
  Object.defineProperty(Person.prototype, "constructor", {
      enumerate: false,
      value: Person 
  });

原型模式:虽然解决了构造函数的缺点问题,但缺点: 1.默认状况下取相同的值;2.引用类型的实例被不少实例共享

为了不这些问题——〉组合使用构造函数和原型模式:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性, 最大限度的节省了内存

function Person(name, age){
  this.name = name;
  this.age = age;
  this.friends = ["A", "B", "C"];
}
Person.prototype = {
  constructor: Person,
  sayName: function(){
        return this.name;
    }
}
var p1 = new Person("Tom", 34);
var p2 = new Person("Joe", 21);
p1.friends.push("D");
p1.friends    //["A", "B", "C", "D"]
p2.friends   //["A", "B", "C"]
p1.sayName === p2.sayName;  //true

组合模式:优势:集两模式优势于一身,极好!所以该模式使用最普遍、认知度最高。

东西一好,就有人挑骨头,其余oo语言开发经验者看到独立的构造函数与原型会以为很困惑,因而...

出现了——>动态原型模式:将全部信息封装在构造函数中

function Person(name, age){
  this.name = name;
  this.age = age;
  this.friends = ["A", "B", "C"];
  if (typeof this.sayName != "function") {
    Person.prototype.sayName = function(){
        return this.name;
    }
  } 
}

//建立第一个实例才会执行,此后,原型完成初始化
var p1 = new Person("Tom", 23);
p1.sayName();  //"Tom"

至此,若是都不适用的话...

出现了——〉寄生构造函数模式:建立一个函数,做用仅仅是封装建立对象的代码,而后再返回新建立的对象

function Person(name, age){            
  var o = new Object();
  o.name = name;
  o.age = age;
  o.sayName = function(){
    return this.name;
  }
  return o;
}
var p1 = new Person("Tom", 23);
p1.sayName();

该模式准确的说与工厂模式如出一辙。只是把该函数当构造函数调用(使用new)而与构造函数模式类似,又有点不一样,这不一样之处就是:显示的建立对象,重写了返回值。

既然如此,这个模式的优点在哪里呢?对于构造函数模式而言,它能够new一个除object类型以外的其余类型——在不扩展原生构造函数的状况下自定义一个扩展型的构造函数。

function SpecialArray(){
   var values = new Array();
   values.push.apply(values, arguments);
   values.toPipedString = function(){
     return this.join("|"); 
   };
   return values;
}
var friends = new SpecialArray("A", "B", "C");
friends.toPipedString();  //"A|B|C"
friends instanceof SpecialArray  //false
friends instanceof Array  //true

说明:构造函数返回的对象与构造函数或者构造函数的原型之间没有任何关系,也就是说构造函数外面建立或者里面建立是没有区别的,(只是寄生在构造函数中)所以没法用instanceof来识别对象。

还有一种建立对象的方法:——>稳妥构造函数模式:没有公共属性,不使用this、new,适合安全环境下使用

function Person(name, age){
}
function Person(name){
  var o = new Object();
  o.sayName = function(){
     return name;
  }
  return o;
}
var p1 = Person("Tom");
var p2 = Person("Joe");
p1.name  //undefined
p2.name  //undefined
p1.sayName()  //"Tom"
p2.sayName()  //"Joe"       传入构造函数的name之恩那个经过sayName()访问

稳妥构造函数模式与寄生构造函数模式相似:建立的对象与构造函数之间没有任何关系。因此没法用instanceof来识别对象。

该博客与js高级程序设计内容类似,加之本身的理解与总结,不对之处欢迎指出。详情可看js高级程序设计。

相关文章
相关标签/搜索