readme:笔记内容来自《JavaScript高级程序设计》(第3版)第6章的内容;大致内容可直接看小结部分;
对象的创建方式
这些特征是为了实现JS引擎用的,因此JS中不能直接访问它们,为了表示特征是内部值,使用方括号类似 [[Enumerable]] 的方法表示;两种属性:数据属性、访问器属性;
包含数据值
只能通过Object.defineProperty()方法修改属性默认的特性
var person = {};
// person:属性所在的对象
// name:属性名称
// {}:描述符对象 -> 修改属性特性(configurable、enummerable、writable、value)
Object.defineProperty(person,"name",{
value : "Jason",
});
// 获取person的描述符对象
var decriptor = Object.getOwnPropertyDescriptor(person,"name");
// 特性的默认值
console.log(decriptor.configurable);//false
console.log(decriptor.writable);//false
console.log(decriptor.enumerable);//false
console.log(decriptor.value);//Jason
console.log(person.name);//Jason
// 但我们后面会知道,用户自定以的函数中的 configurable,writable,enumerablede的默认值均为false
如果手动将 configurable 设置为 false,那么不能再重新配置为 true;
不包含数据值,包含一对 getter() 和 setter() 函数
var book = {
_year : 2004,
edition : 1
};
Object.defineProperty(book,"year",{
// 在严格模式下,需要 getter 和 setter 函数一起定义
get: function(){
return this._year;
},
set: function(newValue){
if(newValue > 2004){
this._year = newValue;
this.edition += newValue - 2004;
}
}
})
// 获取描述符对象
var decriptor = Object.getOwnPropertyDescriptor(book,"year");
// 特性的默认值
console.log(decriptor.configurable);//false
console.log(decriptor.enumerable);//false
console.log(typeof decriptor.get);//function
console.log(typeof decriptor.set);//function
book.year = 2005;
console.log(book.edition); //2
// 除了Object.defineProperty(),也可以使用 _defineGetter_() 和 _defineSetter_()
// book._defineGetter_("year",function(){})
Object.defineProperties()
var book = {};
Object.defineProperties(book,{
_year: {
value : 2004
},
edition: {
value : 1
},
year: {
get: function(){
return this._year;
},
set: function(newValue){
if(newValue > 2004){
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
})
一个属性不能同时具有数据属性和访问器属性特性,下面是错误的示范
//
var book = {};
Object.defineProperties(book,{
year: {
value: 2006,
get: function(){
}
}
});
Object.getOwnPropertyDescriptor() --> 返回一个对象,前面有对应的语法
function createPerson(name,age,job){
var o = new Object;
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
var person1 = createPerson("Jason",18,"enginner");
person1.sayName();// Jason
console.log(typeof person1);// object
特点:
创建步骤:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
}
// this.sayName = new Function("console.log(this.name)");
}
var person1 = new Person("Jason",18,"enginner");
var person2 = new Person("Gero",19,"enginner");
person1.sayName();// Jason
console.log(person1 instanceof Person);// true
console.log(person1.constructor == Person);// true
console.log(person1.sayName == person2.sayName);//false
// 为了使对象的方法为同一个对象,可以将sayName定义为外部函数
function sayName(){
console.log(this.name);
}
this.sayName = sayName;
// 但是对象如果要定义很多方法,那么就要定义很多个全局函数,可以使用原型模式
每个函数都有一个 prototype 属性。这个属性是一个指针,指向一个对象;
用途:包含可以由特定类型的所有实例共享的属性和方法;
function Person(){
Person.prototype.name = "Jason";
Person.prototype.age = 19;
Person.prototype.job = "enginner";
Person.prototype.sayName = function(){
console.log(this.name);
}
// this.sayName = new Function("console.log(this.name)");
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName == person2.sayName);// true
只要创建一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,指向函数的原型对象
这个原型对象会自动获得一个 constructor 属性,指向 prototype 属性所在函数的指针(Person.prototype.constructor 指向 Person)
没有标准的方式访问[[ Prototype ]],但是 FireFox、Safari、Chrome 在每个对象上都支持一个属性 proto
需要明确一点的是:这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间
isPrototypeOf() 方法和 getPrototypeOf() 方法
// 判断person1的[[Prototype]]是否指向Person.prototype
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//true
// 另一个方法 getPrototypeOf()方法返回[[Prototype]];ES5新增
console.log(Object.getPrototypeOf(person1) == Person.prototype);// true
属性搜索的顺序
从对象实例本身开始,如果找到给定名称的属性,则返回该属性的值;如果没有,则继续搜索指针指向原型对象。
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。
使用 delete() 可以完全删除实例属性,但保留了原型中的值
// 给实例赋值,会屏蔽原型中的值,但不会覆盖
function Person(){
Person.prototype.name = "Jason";
Person.prototype.age = 19;
Person.prototype.job = "enginner";
Person.prototype.sayName = function(){
console.log(this.name);
}
// this.sayName = new Function("console.log(this.name)");
}
// 注意:不能直接Person.sayName() 或 Person.protype.sayName();
var person1 = new Person();
var person2 = new Person();
person1.name = "JJ";
console.log(person1.name);// JJ
console.log(person2.name);// Jason
delete(person1.name);
console.log(person1.name); // Jason
hasOwnProperty()方法
用于检测一个属性是存在于实例中,还是存在于原型中。只有当属性存在于实例中时,才返回true;
不能用此方法来检测一个属性是否存在于原型中(可以使用 in 操作符 和 hasOwnProperty())
// 给实例赋值,会屏蔽原型中的值,但不会覆盖
function Person(){
Person.prototype.name = "Jason";
Person.prototype.age = 19;
Person.prototype.job = "enginner";
Person.prototype.sayName = function(){
console.log(this.name);
}
// this.sayName = new Function("console.log(this.name)");
}
var person1 = new Person();
console.log(person1.hasOwnProperty("name"));// false --> 来自原型
person1.name = "JJ";
console.log(person1.hasOwnProperty("name"));// true --> 来自实例
in
in 操作符会在通过对象能够访问给定属性时返回 true。无论是实例中还是原型中。
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name) && (name in object);
}
for-in
for-in:返回的是所有能够通过对象访问的、可枚举的属性,包括存在实例和原型重的属性;
根据规定,所有开发人员定义的属性都是可枚举的;但对于使用 Object.defineProperty()定义的属性来说,enumerable默认为false
var o = {}
o.sayName = function(){console.log("hello")};
var descriptor = Object.getOwnPropertyDescriptor(o,"sayName");
console.log(descriptor.enumerable);// true
for(var prop in o){
if(prop == "sayName"){
console.log(prop);// sayName
}
}
// 使用Object.defineProperty()定义属性
Object.defineProperty(o,"sayHello",{
value:function(){console.log("hello")}
})
var descriptor = Object.getOwnPropertyDescriptor(o,"sayHello");
console.log(descriptor.enumerable);//false
for(var prop in o){
if(prop == "sayHello"){
consoloe.log(prop);// 此行不会被打印
}
}
Objecy.keys()
返回一个包含所有可枚举属性的字符串数组
function Person(){
Person.prototype.name = "Jason";
Person.prototype.age = 19;
Person.prototype.job = "enginner";
Person.prototype.sayName = function(){
console.log(this.name);
}
}
Person();// 在 Chrome 上不写这句或者没有调用过 Person() 输出的 keys 为空
var keys = Object.keys(Person.prototype);
console.log(keys);// ["name", "age", "job", "sayName"]
Object.getOwnPropertyNames()
获取所有实例的属性,包括不可枚举的属性
console.log(Object.getOwnPropertyNames(Person));
// ["constructor", "name", "age", "job", "sayName"]
我们可以使用对象字面量重写原型对象
function Person(){
Person.prototype = {
name : "Jason";
age : 19;
job : "enginner";
sayName : function(){
console.log(this.name);
}
}
}
function Person(){}
// 注意是在函数的外部定义,内部定义则直接定义 prototype
Person.prototype = {
name : "Jason",
age : 19,
job : "enginner",
sayName : function(){
console.log(this.name);
}
}
var friend = new Person();
console.log(friend instanceof Object);// true
console.log(friend instanceof Person);// true
console.log(friend.constructor == Person);// false
console.log(friend.constructor == Object);// true
// 如果 constructor 的值真的很重要,可以在 prototype 中添加该值
Person.prototype = {
constructor : Person,
//...省略
}
function Person(){}
Person.prototype = {
constructor : Person,
name : "Jason",
age : 19,
job : "enginner",
sayName : function(){
console.log(this.name);
}
}
var descriptor = Object.getOwnPropertyDescriptor(Person.prototype,"constructor");
console.log(descriptor.enumerable);// false
for(var prop in Person.prototype){
if(prop == "constructor")
console.log(prop);// 此行不会被输出
}
我们对原型对象所做的任何修改都能立即从实例上反映出来,即使是先创建实例后修改原型。
var friends = new Person();
Person.prototype.sayHi = function(){
console.log("Hi!");
}
friends.sayHi();// Hi!
Person.prototype = {
constructor : Person,
sayHi : function(){
console.log("hi");
}
}
friends.sayHi();
var friends = new Person();
friends2.sayHi();
// 此时的 friends 和 friends2 指向不同的原型对象
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且可以定义新的方法;
console.log(typeof Array.prototype.sort);// function
console.log(typeof String.prototype.substring);// function
String.prototype.startsWith = function(text){
return this.indexOf(text) == 0;
}
var msg = "Hello world";
console.log(msg.startsWith(msg));// true
function Person(){}
Person.prototype = {
constructor : Person,
name : "Jason",
age : 19,
job : "enginner",
friends:["Shelby","Court"],
sayName : function(){
console.log(this.name);
}
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push("van");
console.log(person2.friends);
// ["Shelby", "Court", "van"]
console.log(person1.friends == person2.friends);// true
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性;
function Person(name,age,job){
this.name = name,
this.age = age,
this.job = job,
this.friends = []
}
Person.prototype = {
constructor : Person,
sayName : function(){
console.log(this.name);
}
}
var person1 = new Person("Jason",18,"enginner");
var person2 = new Person("Tom",19,"enginner");
person1.friends.push("JJ");
person2.friends.push("Tony");
console.log(person1.friends);// ["JJ"]
console.log(person2.friends);// ["Tony"]
console.log(person1.sayName == person2.sayName);// true
将所有的信息都封装在了构造函数中,通过构造函数中初始化原型(仅在必要的情况下)
可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = [];
// 只有在sayName()方法不存在的情况下,才会添加到原型中
if(typeof this.sayName != "function"){
Person.prototype = function(){
console.log(this.name);
}
}
}
var person1 = new Person("Jason",18,"enginner");
person1.sayName();
基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后返回新创建的对象;
// 这其实和使用工厂模式创建对象方式是一样的
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
function SpcialArray(){
var values = new Array();
// 添加值
values.push.apply(values,arguments);
// 添加方法
values.toPipedString = function(){
return this.join("|");
};
return values;
}
var colors = SpcialArray("red","blue","green");
console.log(colors.toPipedString());// red|blue|green
console.log(typeof colors);
console.log(colors instanceof Array);// true
console.log(colors instanceof SpcialArray);// false
所谓的稳妥对象字指的是没有公共属性,而且其方法也不引用 this 的对象
所以适合在一些安全的环境中(禁用this和new的环境),或者防止数据被其它应用程序改动时使用。
function Person(name,age,job){
var o = new Object();
//此处定义私有变量和函数
o.sayName : function(){
console.log(name);// 此处不是this.name
}
return o;
}
var person1 = Person("Jason",19,"enginner");
console.log(person1.name);// undefined
person1.sayName();// Jason
继承的方式:接口继承(只继承方法签名) 和 实现继承(继承实际的方法)
由于函数没有签名,所以ECMAScript只支持实现继承;
ECMAScript 描述了原型链的概念,并将原型链作为实现继承的主要方法;
每个构造函数都有一个原型对象 (xxx.prototype),原型对象都包含一个指向构造函数的指针(xxx.prototype.constructor),而实例都包含一个指向原型对象的内部指针 [[Prototype]] ;
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
this.subProperty = false;
}
// 继承了 SuperType --> 具有了 SuperType的所有属性和方法
// 注意事项:
// 1. 此时的constructor不再指向SubType(重写了原型对象),而是指向了SuperType
// 2. 给原型添加方法的代码一定要放在替换原型的语句之后
// 3. 不能使用对象字面量创建原型方法
SubType.prototype = new SuperType();
// SubType.prototype = {} 错误示范,会重写原型对象
SubType.prototype.getSubValue = function(){
return this.subProperty;
}
var instance = new SubType();
console.log(instance.getSuperValue());// true
console.log(instance instanceof Object);// true
console.log(instance instanceof SuperType);// true
console.log(instance instanceof SubType);// true
// 只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型
console.log(Object.prototype.isPrototype(instance));// true
console.log(SuperType.prototype.isPrototype(instance));// true
console.log(subType.prototype.isPrototype(instance));// true
给原型添加方法的代码一定要放在替换原型的语句之后
不能使用对象字面量创建原型方法(因为这样重写了原型,将 SuperType 的实例作为 SubType 的原型对象也就没有意义了)
- 主要的问题来自包含引用类型值的原型;
function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
}
// 此时的所有SubType都共用一个原型对象,即也共用一个 colors
SubType.prototype = new SuperType();
var instance = new SubType();
instance.colors.push("black");
console.log(instance.colors); //["red", "blue", "green", "black"]
var instance1 = new SubType();
console.log(instance1.colors); //["red", "blue", "green", "black"]
- 在创建子类型的实例时,不能向超类型的构造函数中传递参数。应该说没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
所以实际上很少会单独使用原型链。
有时也叫伪造对象或经典继承;
function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
// 继承了 SuperType --> 借调了操类型的构造函数
SuperType.call(this); // 使用apply()也可以
}
var instance = new SubType();
instance.colors.push("black");
console.log(instance.colors); //["red", "blue", "green", "black"]
var instance1 = new SubType();
console.log(instance1.colors); //["red", "blue", "green"]
接用构造函数的很大一个优势是可以在子类型构造函数中向超类型构造函数传递参数;
function SuperType(name){
this.name = name;
}
Supertype.prototype.sayHi = function(){
console.log("Hi!")
}
function SubType(){
SuperType.call(this,"Jason");
this.age = 29;
}
var insatance = new SubType();
console.log(instance.name);// 29
console.log(instance.age );// Jason
instance.sayHi(); // 此时会报错。子类型无法访问到父类型的原型
无法避免构造函数模式存在的问题:方法都在构造函数中定义,因此函数复用就无从谈起。
在超类型的原型中定义的防范,对子类型而言也是不可见的(子类型无法访问到父类型的原型),结果所有类型都只能使用构造函数模式;
也叫伪经典继承,指的是将原型链和接用构造函数的技术组合在一块。即使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var instance1 = new SubType("Jason",29);
instance1.colors.push("black");
console.log(instance1.colors);// ["red", "blue", "green", "black"]
instance1.sayName();// Jason
instance1.sayAge();// 29
var instance2 = new SubType("Greg",27);
console.log(instance2.colors);// ["red", "blue", "green"]
instance2.sayName();// Greg
instance2.sayAge();// 27
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型;
function object(o){
function F(){};
F.prototype = o;
return new F();
}
var person = {
name : "Jason",
friends:["Shelby","Court","Van"]
};
// 相当于对 person 的浅复制(只针对引用类型)
var anotherPerson = object(person);
anotherPerson.name = "Tom";
anotherPerson.friends.push("Gerg");
console.log(person.friends); // ["Shelby", "Court", "Van", "Gerg"]
console.log(person.name); // Jason
要求必须有一个对象可以作为另一个对象的基础;
此处的friends不仅归person所有,也归anotherPerson所有
Object.create()
和object()方法的行为相同;ES5
var person = {
name : "Jason",
friends:["Shelby","Court","Van"]
};
// 相当于对 person 的浅复制(只针对引用类型)
var anotherPerson = Object.create(person);
anotherPerson.name = "Tom";
anotherPerson.friends.push("Gerg");
console.log(person.friends); // ["Shelby", "Court", "Van", "Gerg"]
console.log(person.name); // Jason
// Object.create()可以接收第二个参数,和Object.defineProperties()方法的第二个参数格式相同
var yetAnotherPerson = Object.create(person,{
name : {
value : "Tom"
},
friends: {
value : ["Lucy","Tony"]
}
});
console.log(person.friends);// ["Shelby", "Court", "Van", "Gerg"]
console.log(yetAnotherPerson.friends);// ["Lucy", "Tony"] --> 此时只是修改了 yetAnotherPerson的friends
function createAnother(original){
// 此时的object不是必须的,可以直接在original上添加方法
var clone = object(original);
clone.sayHi = function(){
console.log("hi");
}
return clone;
}
var person = {
name: "Nicholas",
friends: ["Lucy","Tony"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();
使用寄生式继承来为对象添加函数,不能做到函数的复用,这一点和构造函数模式类似
组合继承最大的问题是无论什么情况下,都会调用两次超类型构造函数
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型函数的混成形式来继承方法。
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name);// 第二次调用SuperType()
this.age = age;
}
SubType.prototype = new SuperType();// 第一次调用SuperType()
SubType.prototype.sayAge = function(){
console.log(this.age);
}
// 其实就是直接操作了SuperType的原型对象,而不通过构造函数
function inheritPrototype(superType,subTypr){
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name); // 只调用了一次SuperType的构造函数
this.age = age;
}
inheritPrototype(SuperType,SubType);
SubType.prototype.sayAge = function(){
console.log(this.age);
}
开发人员普遍认为寄生组合继承是引用类型最理想的继承方式;
function createPerson(){
var o = new Object();
o.name = "Jason";
return o;
}
function Person(name,age){
this.name = name;
this.age = age;
}
var person = new Person();
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype = {
constructor : Person,
sayName : function(){
console.log(this.name);
}
}
var person = new Person("Jason",18);
person.sayName();
我们在创建实例对象后修改原型对象将会立即放映到实例上(不能使用对象字面量)
Person.prototype.sayHi = function(){
console.log("Hi!" + this.name);
};
person.sayHi();
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = [];
// 只有在sayName()方法不存在的情况下,才会添加到原型中
// 在第一次调用构造方法时执行,只执行一次
// 这样做的好处仅仅是在构造函数中就封装好原型对象吧。。
if(typeof this.sayName != "function"){
Person.prototype = function(){
console.log(this.name);
}
}
}
var person1 = new Person("Jason",18,"enginner");
person1.sayName();
// 寄生在原有的引用类型上,并对其添加属性或方法
function SpcialArray(){
var values = new Array();
// 添加值
values.push.apply(values,arguments);
// 添加方法
values.toPipedString = function(){
return this.join("|");
};
return values;
}
var colors = new SpcialArray("red","blue","green");
console.log(colors.toPipedString());// red|blue|green
console.log(colors instanceof Array);// true
console.log(colors instanceof SpcialArray);// false -> 并没有通过SpcialArray的构造函数
// 此模式安全,不使用this和new关键字
// 只能通过调用函数内部的方法来访问成员变量(值)
function Person(name,age,job){
var o = new Object();
//此处定义私有变量和函数 (外部只能通过该方法访问name)
o.sayName : function(){
console.log(name);// 此处不是this.name
}
return o;
}
var person1 = Person("Jason",19,"enginner");
console.log(person1.name);// undefined
person1.sayName();// Jason
// 父类型
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
// 子类型
function SubType(){
this.subProperty = false;
}
// 子类型的原型指向新的父类型实例
SubType.prototype = new SuperType();
// SubType.prototype = {} 错误示范,会重写原型对象
SubType.prototype.getSubValue = function(){
return this.subProperty;
}
var instance = new SubType();
console.log(instance.getSuperValue());// true
使用()call/apply()
function SuperType(){
// 此处的colors不是实例共享的属性(有this)
this.colors = ["red","blue","green"];
}
function SubType(){
// 解决了向父类型构造函数传递参数的问题
// 继承了 SuperType --> 借调了操类型的构造函数
SuperType.call(this); // 使用apply()也可以
}
var instance = new SubType();
instance.colors.push("black");
console.log(instance.colors); //["red", "blue", "green", "black"]
var instance1 = new SubType();
console.log(instance1.colors); //["red", "blue", "green"]
主要是针对父子类型中的引用类型;(两次调用父类型的构造函数)
使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
// 如果使用借用构造函数来实现。子类型将不能访问到该方法
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name); // 第一次调用 -> 通过借用构造函数来实现对实例属性的继承
this.age = age;
}
SubType.prototype = new SuperType(); // 第二次调用 -> 使用原型链实现对原型属性和方法(sayName)的继承
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var instance1 = new SubType("Jason",29);
instance1.colors.push("black");
console.log(instance1.colors);// ["red", "blue", "green", "black"]
instance1.sayName();// Jason
instance1.sayAge();// 29
var instance2 = new SubType("Greg",27);
console.log(instance2.colors);// ["red", "blue", "green"]
instance2.sayName();// Greg
instance2.sayAge();// 27
// 浅复制;ES5的Object.create()和该方法类似; -> 只针对引用类型
// 理解基本类型和引用类型作为参数传递时的区别即可
function object(o){
function F(){};
F.prototype = o;
return new F();
}
var person = {
name : "Jason",
friends:["Shelby","Court","Van"]
};
// 相当于对 person 的浅复制(只针对引用类型)
var anotherPerson = object(person);
anotherPerson.name = "Tom";
anotherPerson.friends.push("Gerg");
console.log(person.friends); // ["Shelby", "Court", "Van", "Gerg"]
console.log(person.name); // Jason --> 基本类型不变
function createAnother(original){
// 此时的object不是必须的,可以直接在original添加方法(达到复用?)
var clone = object(original);
clone.sayHi = function(){
console.log("hi");
}
return clone;
}
var person = {
name: "Nicholas",
friends: ["Lucy","Tony"]
};
var anotherPerson = createAnother(person); // 寄生在person实例上添加了sayHi()方法
anotherPerson.sayHi();
只调用了一次父类型的构造函数
// 其实就是直接操作了SuperType的原型对象,而不通过构造函数(可能有点绕)
function object(o){
function f(){};
f.prototype = o;
// 注意是new,而不是直接返回啊。。。
return new f();
}
function inheritPrototype(superType,subType){
//superType.prototype.constructor = subType;
//subType.prototype = superType.prototype;
// 或直接使用 Object.create()
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name); // 只调用了一次SuperType的构造函数
this.age = age;
}
inheritPrototype(SuperType,SubType); // SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var subType = new SubType("Jason",19);
subType.sayName();