第六章 面向对象的程序设计(js高级程序设计)

2019-02-20  本文已影响0人  简默丶XS

Object-Oriented 面向对象

理解对象

  1. configurable 描述该属性能否通过delete删除或被重新定义(为false时defineProperty也无法使用了)
  2. enumerable 能否被for-in循环返回属性
  3. get 在读取属性时调用的函数。
  4. set 在写入属性时调用的函数。
当获取_name值时,调用get方法得到name的值。当设置_name值时,其实时将name值改为了‘默认值’

_ name 前面 _ 的书写代表只能通过对象方法访问的属性。

上述book对象的_ year属性的属性描述符

其他创建对象的方法

1. 工厂模式
这种模式抽象了创建具体对象的过程,用函数来封装以特定接口创建对象的细节。

工厂模式用函数封装了创建对象的过程
缺点:工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

2. 构造函数模式
构造函数可以用来创建特定类型的对象

书中的案例 sayName其实做了同一件事,但是却被创建成了两个不同的function,浪费 将sayName拎出来

3. 原型模式

关系图 实例的sayName均来自构造函数的原型 构造函数同样实现效果

回到问题本质,与构造函数的区别在哪里?
新对象的这些属性和方法是由所有实例共享(不会再创建相同作用的函数)的。换句话说,person1 和 person2 访问的都是同一组属性和同一个 sayName()函数

我的关系图:person1实例与构造函数没有直接关系,person1实例可以调用sayName是因为他在原型对象上进行查找.!注意这里有个错误:应该是__proto__。两个下划线! 书中关系图
  1. isPrototypeOf()判断对象是否和原型对象有关
  2. Object.getPrototypeOf()获得对象的原型对象

alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true

Object.getPrototypeOf(person1) == Object.getPrototypeOf(person1) //获得person1对象指向的原型对象

person1实例上name属性来自本身还是来自于原型对象上
alert(person1.hasOwnProperty("name")); //false 
alert("name" in person1); //true name属性在实例对象属性中
var person = new Person(); 
alert(hasPrototypeProperty(person, "name")); //true  
person.name = "Greg"; 
alert(hasPrototypeProperty(person, "name")); //false  实例对象要能访问到原型上的属性才能返回true
var person = new Person()
Object.key(person) //"say"  person实例对象上的属性,原型对象上的不会被
console.log(Object.keys(person.__proto__))// "age,job,name,sayName" 原型上的属性
console.log(Object.getOwnPropertyNames(person.__proto__.__proto__))
function Person(){ 
} 
Person.prototype = { 
 constructor : Person,  //如果没有这句,constructor 属性就指不到构造函数了
 name : "Nicholas", 
 age : 29, 
 job: "Software Engineer", 
 sayName : function () { 
 alert(this.name); 
 } 
}; 

此方法带来的问题:constructor 的[[Enumerable]]值为true可枚举了

使用Object.defineProperty重设构造函数

Object.defineProperty(Person.prototype, "constructor", { 
 enumerable: false, 
 value: Person 
}); //此写法适用于es5
var friend = new Person(); 
Person.prototype.sayHi = function(){ 
 alert("hi"); 
}; 
friend.sayHi(); //"hi"(没有问题!)

当然,你不能重写原型对象,切断与构造函数的联系

function Person(){ 
} 
var friend = new Person(); 

Person.prototype = { 
 constructor: Person, 
 name : "Nicholas", 
 age : 29, 
 job : "Software Engineer", 
 sayName : function () { 
 alert(this.name); 
 } 
}; 
friend.sayName(); //error 
在new 构造函数时,构造函数会为实例创造指针__proto__指向原型对象。重写原型后,旧的实例依然和旧的原型保持着联系,和新的原型没有联系
String.prototype.startsWith() =function(){
  ...//你可以给原生对象添加属性,让所有实例化的字符串都能访问,但是不建议这样做
}

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

function Person(name, age, job){ 
 this.name = name; 
 this.age = age; 
 this.job = job; 
 this.friends = ["Shelby", "Court"]; 
} 
Person.prototype = { 
 constructor : Person, 
 sayName : function(){ 
 alert(this.name); 
 } 
} 

5. 动态原型模式:在构造函数中使用原型模式,并且避免重复挂载到原型模式
它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点
换句话说,第一次new Person()的时候,sayName肯定是不存在的,所以会挂载sayName和其他方法到原型上,而下一次newPerson的时候,sayName()已经在原型上了[注意if判断的作用],sayName包括其他if语句中的方法都不会被重复挂载

  function Person(name, age, job) {
    //属性
    this.name = name;
    this.age = age;
    this.job = job; //方法
    if (typeof this.sayName != "function") {
      Person.prototype.sayName = function () {
        alert(this.name);
      };

      Person.prototype.sayAge = function () {
        alert(this.age);
      };
    }
  }

6. 寄生构造函数模式
类似于工厂模式,不过使用new function()的形式创建

function Person(name, age, job){ 
 var o = new Object(); 
 o.name = name; 
 o.age = age; 
 o.job = job; 
 o.sayName = function(){ 
 alert(this.name); 
 }; 
 return o; 
} 
var friend = new Person("Nicholas", 29, "Software Engineer"); 
friend.sayName(); //"Nicholas" 

这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊
数组。由于不能直接修改 Array 构造函数,因此可以使用这个模式
注意:返回的对象与构造函数或者与构造函数的原型属性之间没有关系。 instanceof 不能溯源,所以建议在可以使用其他模式的情况下,不要使用这种模式。

7. 稳妥构造函数模式(闭包)

function Person(name, age, job){ 
 
 //创建要返回的对象
 var o = new Object(); 
 //可以在这里定义私有变量和函数
 //添加方法
 o.sayName = function(){ 
 alert(name); 
 }; 
 //返回对象
 return o; 
} 
var friend = Person("Nicholas", 29, "Software Engineer"); 
friend.sayName(); //"Nicholas" 

除了使用 sayName()方法之外,没有其他办法访问 name 的值(有点用到了闭包,让匿名函数返回私有变量)
同样和寄生构造模式一样不能溯源,不能使用instanceof

继承

public static void main(String[] args)

1.原型链

继承图解A继承B
alert(instance instanceof Object); //true 
alert(instance instanceof SuperType); //true 
alert(instance instanceof SubType); //true
  1. isPrototypeOf
alert(Object.prototype.isPrototypeOf(instance)); //true 
alert(SuperType.prototype.isPrototypeOf(instance)); //true 
alert(SubType.prototype.isPrototypeOf(instance)); //true 

2.借用构造函数

function SuperType(name){ 
 this.name = name; 
} 
function SubType(){ 
 //继承了 SuperType,同时还传递了参数
 SuperType.call(this, "Nicholas"); 
 
 //实例属性
 this.age = 29; 
} 
var instance = new SubType(); 
alert(instance.name); //"Nicholas"; 
alert(instance.age); //29 

3.组合继承

  function f1(name) {
    this.name = name
    this.colors = ['blue', 'red', 'green'] //将需要被继承的引用类型定义在构造函数中
  }

  f1.prototype.sayName = function () {
    alert(this.name)
  }

  function f2(name, age) {
    f1.call(this, name)  //此时,f2上已经继承f1构造函数中的属性
    this.age = age
  }

  f2.prototype = new f1(); //此时,f2已经继承f1原型对象上的属性

  var F2 = new f2()

  for (const x in F2) {
    console.log(x)//name color age sayname
  }

4.原型式继承

  function object(o) {
    function F() { }
    F.prototype = o; //person对象被共享到了通过object创造出来的对象的原型上
    return new F();
  }
  var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
  };
  debugger
  var anotherPerson = object(person);
  anotherPerson.name = "Greg";
  anotherPerson.friends.push("Rob");
  var yetAnotherPerson = object(person);
  yetAnotherPerson.name = "Linda";
  yetAnotherPerson.friends.push("Barbie");
  alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 
var person = { 
 name: "Nicholas", 
 friends: ["Shelby", "Court", "Van"] 
}; 
var anotherPerson = Object.create(person); 
anotherPerson.name = "Greg"; 
anotherPerson.friends.push("Rob"); 
 
var yetAnotherPerson = Object.create(person); 
yetAnotherPerson.name = "Linda"; 
yetAnotherPerson.friends.push("Barbie"); 
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 

在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式
继承是完全可以胜任的.

5.寄生式继承

  function createAnother(original) {
    var clone = Object.create(original); //通过调用函数创建一个新对象
    clone.sayHi = function () { //以某种方式来增强这个对象
      alert("hi");
    };
    return clone; //返回这个对象
  }
  var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
  };
  var anotherPerson = createAnother(person);
  anotherPerson.sayHi(); //"hi" 

注意点: clone.sayHi 可以给新对象上添加属性,和寄生构造函数模式有点像,createAnother存在的意义就是在新的实例上添加公有属性

6.寄生组合式继承

function inheritPrototype(subType, superType){ 
 var prototype = Object.creat(superType.prototype); //创建对象
 prototype.constructor = subType; //增强对象
 subType.prototype = prototype; //指定对象
} 
function SuperType(name){ 
 this.name = name; 
 this.colors = ["red", "blue", "green"]; 
} 
SuperType.prototype.sayName = function(){ 
 alert(this.name); 
}; 
function SubType(name, age){ 
 SuperType.call(this, name); 
 this.age = age; 
} 
inheritPrototype(SubType, SuperType);   //SubType.protoype = new SuperType();
SubType.prototype.sayAge = function(){ 
 alert(this.age); 
}; 

ps:终于看完了,真特么累,感觉迷迷糊糊的,肯定要刷第二遍的~。

上一篇 下一篇

猜你喜欢

热点阅读