Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

js面对对象(创建对象,实现继承) #31

Open
aermin opened this issue Feb 27, 2018 · 0 comments
Open

js面对对象(创建对象,实现继承) #31

aermin opened this issue Feb 27, 2018 · 0 comments

Comments

@aermin
Copy link
Owner

aermin commented Feb 27, 2018

创建对象

创建单个对象可以用对象字面量,object构造函数(new Object() )方法来创建,为啥还要单独拿出来讲呢?因为这些方式有个明显的缺点:使用同 一个接口创建很多对象,会产生大量的重复代码。

为了写质量更高的代码,需要学习这些创建对象模式。

创建对象是下文实现继承的基础。

工厂模式 (创建对象)

也是一种是软件工程领域一种广为人知的设计模式

思路:在一个函数内创建一个空对象,给空对象添加属性和属性值,return这个对象。然后调用这个函数并传入参数来使用。

实例

function createPerson(name, age, job){ 
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){ alert(this.name); }; 
    return o;
}
var person1 = createPerson("xm", 22); 
var person2 = createPerson("Greg", 27);
console.log(person1.name) //xm
console.log(person1.sayName()) //xm

优点:解决了创建 多个相似对象的问题
缺点:没有解决对象识别的问题(即怎样知道一个对象的类型)

构造函数模式(创建对象)

思路:创建一个构造函数,然后用new 创建构造函数的实例。

实例

function Person(name, age, job){  //按照惯例,构造函数应以一个 大写字母开头,而非构造函数应以一个小写字母开头。
    this.name = name; 
    this.age = age; 
    this.sayName = function(){ 
        console.log(this.name); 
    }; 
}

var person1 = new Person("xm", 22); 
var person2 = new Person("Greg", 27);
console.log(person1.name) //xm
console.log(person1.sayName()) //xm

优点:1.子类型构造函数中可向超类型构造函数传递参数。

2.方法都在构造函数中定义,对于属性值是引用类型的就可通过在每个实例上重新创建一遍,避免所有实例的该属性值指向同一堆内存地址,一个改其他也跟着改。
缺点:对于一些可共用的属性方法(比如这边的this.sayName)没必要都在每个实例上重新创建一遍,占用内存。(无法复用)

原型模式(创建对象)

思路:创建一个函数,给函数原型对象赋值。

具体一点就是利用函数的prototype属性指向函数的原型对象,从而在原型对象添加所有实例可共享的属性和方法。

原型图

image

实例

function Person(){ }
Person.prototype.name = "xm"; 
Person.prototype.age = 22; 
Person.prototype.sayName = function(){
     console.log(this.name); 
};
var person1 = new Person();
console.log(person1.name) //xm
console.log(person1.sayName()) //xm

优点:可以让所有对象实例共享它所包含的属性和方法(复用性)。
缺点:1.在创建子类型的实例时,不能向超类型的构造函数中传递参数。

2.如果包含引用类型值的属性,那一个实例改了这个属性(引用类型值),其他实例也跟着改变。

组合使用构造函数模式和原型模式(创建对象,推荐)

思路:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。简单来说就是属性值是引用类型的就用构造函数模式,方法和属性能共享的就用原型模式,取精去糟。

实例

function Person(name, age){ //构造函数模式
    this.name = name; 
    this.age = age; 
    this.friends = ["aa", "bb"]; 
}
Person.prototype = {  //原型模式
    constructor : Person, 
    sayName : function(){ 
        console.log(this.name); 
    }
}
Person.prototype.hobby = {exercise:"ball"}; //原型模式

var person1 = new Person("xm", 22);
var person2 = new Person("Greg", 27)
person1.friends.push("cc");   
console.log(person1.friends);   //"aa,bb,cc"
console.log(person2.friends);   //"aa,bb"
person1.sayName = function(){console.log(this.age)};
person1.sayName();  //22
person2.sayName();  //Greg
person1.hobby.exercise = "running";
console.log(person1.hobby);  //{exercise: "running"}
console.log(person2.hobby); //{exercise: "running"}

注意&疑问:js引用类型有object,数组,函数,这里原型模式为啥object,数组会如实地一个实例改,其他跟着改;而函数却一个实例改,其他没跟着改。有知道答案的麻烦一定要告诉我下😂

优缺点:构造函数模式和原型模式的取精去糟。

继承

原型链(继承)

思路:利用原型让一个引用类型继承另一个引用类型的属性和方法。

原型链图

image

我画成这样更好理解

image

核心code

SubType.prototype = new SuperType();

实例

function SuperType(){ 
    this.colors = ["red", "blue", "green"];
    this.property = true; 
}

SuperType.prototype.getSuperValue = function(){ 
  return this.property; 
};

function SubType(){};
//继承了 SuperType
SubType.prototype = new SuperType();
//添加新方法
SubType.prototype.getSubValue = function (){ 
  return this.subproperty; 
};
//覆盖超类型中的方法(父类的原型方法不受影响,只是在子类中被覆盖)
SubType.prototype.getSuperValue = function (){ 
  return false; 
};
var instance = new SubType(); 
console.log(instance.getSuperValue());  // false
console.log(SuperType.prototype.getSuperValue); // ƒ (){ return this.property; }

var instance1 = new SubType();
var instance2 = new SubType(); 
console.log(instance1.colors); //["red", "blue", "green"]
console.log(instance2.colors);//["red", "blue", "green"]

instance1.colors.push("black"); 
console.log(instance1.colors);//["red", "blue", "green", "black"]
console.log(instance2.colors);//["red", "blue", "green", "black"]

instance1.property = 'test';
console.log(instance1.property);// 'test'
console.log(instance2.property);// true

优点:可以让所有对象实例共享它所包含的属性和方法(复用性)

缺点:
1.在创建子类型的实例时,不能向超类型的构造函数中传递参数。

2.如果包含引用类型值的属性,那一个实例改了这个属性(引用类型值),其他实例也跟着改变。

实际代码体验原型链

function SuperType(){ 
  this.property = true; 
}
SuperType.prototype.getSuperValue = function(){ 
  return this.property; 
};

function SubType(){
  this.subproperty = false; 
}

SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function (){ 
  return this.subproperty; 
};

function Test() { 
  this.testproperty = true; 
}

Test.prototype = new SubType();
var testInstance = new Test();

testInstance.__proto__ === Test.prototype // true
testInstance.__proto__.__proto__ === SubType.prototype // true
testInstance.__proto__.__proto__.__proto__ === SuperType.prototype // true

总结:一个实例的__proto__ 等于这个实例所属的构造函数的prototype

所以解释下下面为何为true?

function Point(x, y) {
    this.x = x;
    this.y = y;
}
var myPoint = new Point();
// the following are all true
myPoint.__proto__ == Point.prototype // true
myPoint.__proto__.__proto__ == Object.prototype // true

这边是因为myPoint.proto. 等于Point.prototype,而Point.prototype是Object的实例{}, 所以等于Object.prototype

借用构造函数(继承)

思路:在子类型构造函数的内部调用超类型构造函数。

核心code

function SubType(){ //SubType继承了 SuperType
     SuperType.call(this,argument); 
}

实例

function SuperType(){ 
    this.colors = ["red", "blue", "green"]; 
}
function SubType(){ //继承了 SuperType 
    SuperType.call(this); 
}

var instance1 = new SubType(); 
instance1.colors.push("black"); 
console.log(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType(); 
console.log(instance2.colors); //"red,blue,green"

优点: 1.子类型构造函数中可向超类型构造函数传递参数。

2.方法都在构造函数中定义,对于属性值是引用类型的就可通过在每个实例上重新创建一遍,避免所有实例的该属性值指向同一堆内存地址,一个改其他也跟着改。
缺点:无法避免构造函数模式存在的问题——方法都在构造函数中定义(无法复用);在超类型的原型中定义的方法,对子类型而言也是不可见的。

组合继承

思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

实例

function SuperType(name){  //父类(构造函数)
    this.name = name;
}

SuperType.prototype.sayName = function(){  //父类的原型添加一个方法
    console.log(this.name);
}

function SubType(name, age){  //借用构造函数来实现对实例属性的继承 
    SuperType.call(this, name);    //继承实例属性 这边继承this.name = name;
    this.age = age;     //自己的属性
}

SubType.prototype = new SuperType();    //使用原型链实现对原型属性和方法的继承  这边是继承
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){ 
    alert(this.age); 
};
var instance1 = new SubType("xm", 22);
console.log(instance1.age)   // 22
console.log(instance1.name)   // xm
instance1.sayName(); //xm
instance1.sayAge(); //22

优缺点:借用构造函数继承和原型链继承的取精去糟。

@aermin aermin changed the title js面对对象 js面对对象(创建对象,实现继承) Feb 27, 2018
@aermin aermin added the js label Feb 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant