理解js的对象,对象创建,继承

js对象概述

理解js对象

js对象的属性在创建时都带有一些特征值,js通过这些特征值来定义它们的行为。

属性类型

1.数据属性

数据属性包含一个数据值的位置,在这个位置可以读取和写入值

2.访问器属性

定义多个属性

读取属性的特性

创建对象

虽然Object构造函数,对象字面量都可以来创建对象,但这些方式有个缺点:在使用同一个接口创建很多对象的时候,会产生大量重复代码,所以在创建对象的时候,可以使用一些模式来进行创建:

工厂模式

/**
 * 工厂模式——创建对象
 * 解决了很多创建类似对象的问题,但却没有解决对象识别的问题
 * @param name
 * @param age
 * @param job
 * @returns {Object}
 */
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("sqliang",23,"IT");

构造函数模式

/**
 * 构造函数模式——创建对象
 * 创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
 * 不同点:
 * 1.没有显示地创建对象
 * 2.直接将属性和方法赋给了this对象
 * 3.没有return语句
 *
 * 构造函数的缺点:会导致不同的作用域链和标识符解析,但
 * 创建Function新实例的机制仍然是相同的
 * 每个方法都要在每个实例上重新创建一遍
 *
 * @param name
 * @param age
 * @param job
 * @constructor
 */
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        console.log(this.name);
    };
}
var person2 = new Person("sqliang2",24,"ITT");

Person("cyy",23,"haha");//作为普通函数调用,添加到window对象
var person3 = new Object();
Person.call(person3,"sqliang",24,"TTI");//在另一个对象作用域中调用Person函数

原型模式

/**
 * 原型模式——创建对象
 *与构造函数不同,新对象的这些属性和方法是由所有实例共享的
 * 原型最初只包含constructor属性,该属性也是共享的
 * @constructor
 */
function PersonWithProto(){
    PersonWithProto.prototype.name = "sqliang";
    PersonWithProto.prototype.age = 23;
    PersonWithProto.prototype.job = "IT";
    PersonWithProto.prototype.sayName = function(){
        console.log(this.name);
    };
}
var per1 = new PersonWithProto(),
    per2 = new PersonWithProto();
//可以通过isPrototypeOf()来确定对象之间是否存在原型关系
console.log(PersonWithProto.prototype.isPrototypeOf(per1));//true
//ES5新增方法getPrototypeOf()来获取Prototype的值
console.log(Object.getPrototypeOf(per2) === Person.prototype);//true
console.log(Object.getPrototypeOf(per2).name);//"sqliang"

//可以通过对象实例访问保存在原型中的值,但是不能通过对象实例修改原型对象中的值
per2.name = "hehe";
console.log(per2.name);//"hehe"
console.log(per1.name);//"sqliang"
delete per2.name;

console.log(per1.hasOwnProperty("name"));//false
console.log("name" in per1);//true

per1.name = "cyy";
console.log(per1.name);//"cyy"
console.log(per1.hasOwnProperty("name"));//true
console.log("name" in per1);//true

//同时使用in 和 hasOwnProperty可以检测该属性是存在于对象中,还是存在于原型中
/**
 * 检测对象是否含有某原型属性
 * @param obj
 * @param name
 * @returns {boolean}
 */
function hasPrototypeProperty(obj,name){
    return !obj.hasOwnProperty(name) && (name in obj);
}
//原型模式更简单的实现
/**
 * 更简单的实现原型模式——创建对象
 * *****************注意:此时constructor属性不再指向Animal了,而是Object*****************
 * ****原因:
 * 每创建一个对象时,会同时创建它的prototype对象,这个对象也会自动获取constructor属性。而下面的
 * 语法,本质上完全重写了默认的constructor属性,不再指向Animal了,指向了Object。此时,尽管instanceof
 * 还可以返回正确的结果,但通过constructor已经无法确定对象的类型了
 * @constructor
 */
function Animal(){}
Animal.prototype = {
    name : 'sqliang',
    age : 23,
    job : 'softwares Engineer',
    sayName : function(){
        console.log(this.name);
    }
};
var ani = new Animal();
console.log(ani instanceof Object);//true
console.log(ani instanceof Animal);//true
console.log(ani.constructor == Animal);//false
console.log(ani.constructor == Object);//true

//因此需要加上constructor属性
/**
 * 这种重设constructor属性会导致他的[[Enumerable]]特性被设置为true。默认情况下,
 * 原生的constructor是不可枚举的
 * @constructor
 */
function Animals(){}
Animals.prototype = {
    constructor : Animals,
    name : 'sqliang',
    age : 18,
    job : 'Softwares Engineer',
    sayName : function(){
        console.log(this.name);
    }
};
//所以,如果使用的是ES5的js引擎,可以重设构造函数,即:
function Animals2(){}
Animals2.prototype = {
    name : 'sqliang',
    age : 18,
    job : 'Softwares Engineer',
    sayName : function(){
        console.log(this.name);
    }
};
//重设构造函数,只适用于ES5兼容的浏览器
Object.defineProperty(Animals2.prototype,'constructor',{
    enumerable : false,
    value : Person
});

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

/**
 * 组合使用构造函数模式和原型模式——创建对象
 * 原型模式用于定义方法和共享的属性,构造函数模式用于定义实例属性。
 * 使得:
 * 每个实例都有自己的一份实例属性的副本,但同时又共享着对方法的引用
 * 最大限度地节省了内存。这种汇合模式还支持向构造函数传递参数,集两种模式之长
 * @param name
 * @param age
 * @param job
 * @constructor
 */
function Man(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
}
//重写了原型属性,要指定constructor,否则指向了Object
Man.prototype = {
    constructor : Man,
    sayName : function(){
        console.log(this.name);
    }
};

动态原型模式(推荐使用的一种模式)

/**
 * 动态原型模式————创建对象
 * 把所有信息封装在构造函数中,通过在构造函数中初始化原型,又
 * 保持了同时使用构造函数和原型的有点,还可以通过检查某个应该存在的方法
 * 是否有效,来决定是否需要初始化原型
 * @param name
 * @param age
 * @param job
 * @constructor
 */
function Per(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["cyy","yyc"];

    //检测
    if(typeof this.sayName != "function"){
        Per.prototype.sayName = function(){
            console.log(this.name);
        }
    }
    Per.prototype.getHand = function(){
        console.log("two hands");
    }
    Per.prototype.hit = function(){
        console.log("I will hit you.");
    }
}

寄生构造函数模式

/**
 * 寄生构造函数模式——创建对象
 * 返回的对象与构造函数或者与构造函数的原型之间没有关系
 *
 * 除了使用时用new操作符,病叫做构造函数之外,与工厂模式没有差别
 * 这个模式可以在特殊情况下用来为对象创建构造函数。如:
 * 创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数,
 * 因此可以使用这个模式。
 * @returns {Array}
 * @constructor
 */
function SpecialArray(){
    var arr = new Array();
    arr.push.apply(arr,arguments);//添加值
    arr.toPipedString = function(){
        return this.join("&");
    }
    return arr;
}
var colors = new SpecialArray("red","blue","green");
console.log(colors.toPipedString());//"red&blue&green"

稳妥构造函数模式

/**
 * 稳妥构造函数模式——创建对象,稳妥对象指没有公共属性,而且其方法也不引用this的对象。
 * 该模式遵循与寄生构造函数类似的模式,但是创建对象的实例方法不引用this,不使用new
 * 操作符调用构造函数
 * 最适合在一些安全环境中(禁止使用this和new的环境),或者在纺织数据被其他应用程序
 * 改动时使用。
 * @param name
 * @param age
 * @constructor
 */
function Wemon(name,age){
    var o = new Object();
    var hello = "Hello";
    o.sayName = function(){
        console.log(name);
    }
    o.sayHello = function(){
        console.log(hello);
        return hello;
    }
}
var nihao= Wemon("sqliang",23);
nihao.sayName();
nihao.sayHello();

继承

/**
* =====================继承
* 许多OO语言都支持两种继承方式:接口继承,实现继承
* 接口继承只继承方法签名,
* 实现继承继承实现的方法。
* *js只支持实现继承,由于函数没有签名,无法实现接口继承*
* js继承主要依靠原型链来实现的
*/

原型链

/**
 * ====================原型链
 * 基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法。
 * 基本模式:让原型对象等于另一个类型的实例
 * 问题:
 * 1,所有子类型的实例会共享超类型的实例属性
 * 2,在创建子类型的实例时,不能像超类型的构造函数传递参数。
 * 即,没有办法在不影响所有对象实例的基础上给超类型传递参数。
 * 因此,实践中很少会单独使用原型链
 * 
 */
function SuperType(){
    this.property = true;
    SuperType.prototype.getSuperValue = function(){
        console.log("Super:" + this.property);
        return this.property;
    }
}
function SubType(){
    this.subProperty = false;
    SubType.prototype.getSubValue = function(){
        console.log("Sub:" + this.subProperty);
        return this.subProperty;

    }
}
//继承了SuperType
SubType.prototype = new SuperType();
var instance = new SubType();
console.log(instance.getSuperValue());

//重写超类型里的方法
SubType.prototype.getSuperValue = function(){
    return false;
}
console.log(instance.getSuperValue());

借用构造函数

/**
 * 继承,借助构造函数
 * 在子类型构造函数内部调用超类型构造函数
 * 借助构造函数的问题:
 * 方法都在构造函数中定义,函数无法复用
 * 在超类型的原型中定义的方法,对子类型是不可见的,
 * 导致所有模式都只能使用构造函数模式
 * (借助构造函数的技术是很少单独使用的)
 */
function SuperColor(){
    this.colors = ["red","green","blue"];
}

function SubColor(){
    //执行了SuperColor(),继承了SuperColor的color属性
    SuperColor.call(this);
}
var ins1 = new SubColor();
ins1.colors.push("black");
console.log(ins1.colors);//["red","green","blue","black"]
var ins2 = new SubColor();
console.log(ins2.colors);//["red","green","blue"]

//传递参数
function SuperMan(name){
    this.name = name;
    SuperMan.prototype.getName = function(){
        console.log(this.name);
        return this.name;
    }
}

function SubMan(){
    //为了确保SuperMan构造函数不会重写子类型的属性,可以在调用超类型构造函数后,
    //再添加应该在子类型中定义的属性
    SuperMan.call(this,"sqliang");
    this.age = 23;
}
var man = new SubMan();
console.log(man.age);

组合式继承

/**
 * 组合继承,(推荐使用)
 * 将原型链和借用构造函数的技术组合到一块,
 * 使用原型链实现对原型属性和方法的继承,借用构造函数来实现对实例属性的继承。
 * @param name
 * @constructor
 */
function SuperStar(name){
    this.name = name;
    this.colors = ["red","blue","green"];
    SuperStar.prototype.getName = function(){
        console.log(this.name);
    }
}

function SubStar(name,age){
    SuperStar.call(this,name);
    this.age = age;
}
//继承方法,先重写,后加sayAge
SubStar.prototype = new SuperStar();
SubStar.prototype.constructor = SubStar;

SubStar.prototype.sayAge = function(){
    console.log(this.age);
};
var star1 = new SubStar("sqliang",23);
star1.colors.push("black");
console.log(star1.colors);//["red","blue","green","black"]
star1.getName();//"sqliang"
star1.sayAge();//23

var star2 = new SubStar("cyy",32);
console.log(star2.colors);//["red", "blue", "green"]
star2.getName();//"cyy"
star2.sayAge();//32

原型式继承

/**
 * 原型式继承(没有严格意义上的构造函数,借助原型基于已有的对象创建新对象
 * 同时还不必因此创建自定义类型)
 * 在这种范式下,person.friends不仅属于person所有,而且也被anotherPerson
 * 和anotherTwo所共享,实际上也就是相当于有创建了person对象的两个副本。
 * 
 */
var person = {
    name : "sqliang",
    friends : ["hello","cyy","lp","gyf","JSF"]
};
var anotherPerson = object(person);
anotherPerson.name = "lsq";
anotherPerson.friends.push("lsq");

var anotherTwo = object(person);
console.log(anotherTwo.name);//"lsq"
console.log(anotherTwo.friends);//["hello","cyy","lp","gyf","JSF","lsq"]

function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}

寄生式继承

/**
 * 寄生式继承(与原型式继承紧密相关的一种思路)
 * 其思路与寄生构造函数和工厂模式类似,即创建一个用于封装继承过程
 * 的函数,函数在内部以某种方式来增强对象,最后再像真的是它做了所有
 * 工作一样返回对象.
 * 在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种
 * 有用的模式,还有object()不是必需的,任何能够返回新对象的函数都适用
 * 于此模式
 */
function createAnother(original){
    var clone = object(original);
    clone.sayHi = function(){
        console.log("Hi");
    }
    return clone;
}
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}

寄生组合式继承(集寄生式和组合继承的优点于一身,是实现基于类型继承的最有效方式)

/**
 * 寄生组合式继承(最有效的,最推荐使用的)
 * 由于组合继承存在不足,即无论在什么情况下,都会调用两次超类型构造函数,
 * 一次是在创建子类型原型时,一次是在子类型构造函数内部。虽然子类型最终会
 * 包含超类型对象的全部实例属性,但是在调用子类型构造函数时重写了这些属性
 * =================而寄生组合式继承,
 * 通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,主要思路是:
 * 不必为了指定子类型的原型而调用超类型构造函数,此时最需要的是超类型原型的一个副本而已。
 * 本质上就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
 * 这样,高效性就体现在之调用了一次超类型构造函数(在子类型构造函数中),并且避免了在
 * 子类型上创建不必要的、多余的属性,原型链还能保持不变。
 * 因此,还可以正常使用instanceof 和isPrototypeOf(),是目前最理想的继承范式
 */
function SuperBest(name){
    this.name = name;
    this.colors = ["red","green","blue"];
    SuperBest.getName = function(){
        return this.name;
    }
}
function SubBest(name, age){
    SuperBest.call(this,name);
    this.age = age;
}
inheritPrototype(SubBest,SuperBest);
SubBest.prototype.sayAge = function(){
    console.log(this.age);
};
function inheritPrototype(subType,superType){
    var prototype = object(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值