大话前端让前端飞前端开发那些事

JS之面向对象和原型

2018-03-14  本文已影响25人  fenerchen

创建对象

1、 工厂模式

若使不同的对象拥有相同的属性和方法,如果每个对象都单独定义一遍,会产生大量代码。工厂模式,就是解决这个问题,而且,每次调用返回的对象相互独立。工厂模式把创建对象封装在一个函数里来实现。

function createPerson(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 person1 = createPerson("Nicholas", 29, "Software Engineer");
person1.sayName()//"Nicholas"
var person2 = createPerson("Greg", 27, "Doctor");
person2.sayName()//"Greg"

千万不要对对象直接复制,下面这样做,box1和box指向同一个对象,其一变,则都变

var box = new Object();
box.age = 100; //创建一个age 属性并赋值
box.run = function () { //创建一个run()方法并返回值
return this.name + this.age + '运行中...';
};
var box1=box;
box1.age=1;
console.log(box.age)//1

工厂模式
优点:解决代码重复(重复化实例)
缺点:无法区分是哪个对象的实例

2、构造函数

与工厂模式的不同:
1.构造函数方法没有显示的创建对象(new Object());
2.直接将属性和方法赋值给this 对象;
3.没有renturn 语句。

function Person(name){
    this.name=name;
    this.sayName=function(){
        console.log(this.name);
    }
}
var p1=new Person('A');
p1.sayName();//A

p1有一个constructor属性,指向Person

console.log(p1.constructor==Person);//true

对象的constructor属性最初是用来标识对象类型的,但是检测对象类型建议使用instanceof。

console.log(p1 instanceof Person);//true
console.log(p1 instanceof Object);//true

构造函数的规范:1、函数名和实例化构造名相同且大写(非强制)2、通过构造函数创建对象,必须用new运算符
构造函数执行过程:
1、当使用了构造函数,并且new构造函数(),那么后台就执行了new Object();
2、将构造函数的作用域给新对象,而函数体内的this就代表new Object()出来的对象。
3、执行构造函数内的代码
4、返回新对象

构造函数优点;解决代码重复,解决对象类别识别问题
缺点:每个方法都要在实例上创造一遍,因为函数也是对象,每定义一个函数,就实例化了一个对象new Function().因此
console.log(p1.sayName==p2.sayName);//false
解决:把函数定义转移到外部,p1,p2共享全局下的同一个sayName()。但是,这样做毫无封装可言

function Person(name){
    this.name=name;
    this.sayName=sayName;
}
function sayName(){
        console.log(this.name);
    }
var p1=new Person('A');
var p2=new Person('b');
console.log(p1.sayName==p2.sayName);//true

3、原型(函数中的一个对象属性)

每个函数有一个prototype属性,这个属性指向函数的原型对象,该属性包含由特定类型的所有实例共享的属性和方法。不妨理解为,prototype是通过调用构造函数而创建的那个实例对象的原型对象。不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

[[Prototype]]在创建实例时,自动生成,他是一个指针,指向构造函数的原型对象,实例与构造函数没有直接关系。通过isPrototypeOf()方法来确定对象之间是否存在这种关系。实例person1/2和原型的constructor均指向构造函数
console.log(Person.prototype.isPrototypeOf(person1))//true

另一个是Object.getPrototypeOf()获取对象的原型

console.log(Object.getPrototypeOf(person1)==Person.prototype)//true

原型模式的执行流程:
1.先查找构造函数实例里的属性或方法,如果有,立刻返回;
2.如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回;
使用hasOwnProperty()方法来检测一个属性是存在于实例中还是原型中,

console.log(person1.hasOwnProperty('name'))//false
person1.name='w';
console.log(person1.hasOwnProperty('name'))//true
console.log(person1.name)//w

in操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中.

console.log(person1.hasOwnProperty('name'))//false
console.log('name' in person1);//true
person1.name='w';
console.log(person1.hasOwnProperty('name'))//true
console.log('name' in person1);//true

为了让属性和方法更好的封装,可用字面量方式,但是要注意,字面量创建的方式使用constructor属性不会指向构造函数,而是指向Object

function Person(){
}
Person.prototype={
    name:'s',
    sayName:function(){
        console.log(this.name);

    }
}
var p1=new Person();
console.log(p1.constructor==Person);//false
console.log(p1.constructor==Object);//true

字面量方式,需要强制constructor指向构造函数

function Person(){
}
Person.prototype={
   constructor:Person,
    name:'s',
    sayName:function(){
        console.log(this.name);

    }
}
var p1=new Person();
console.log(p1.constructor==Person);//true
console.log(p1.constructor==Object);//false

字面量方式为什么constructor 会指向Object?因为,字面量其实就是新创建了一个对象,每创建一个函数,同时会创建他的prototype,Person.prototype这个对象也会自动获取constructor 属性。所以,新对象的constructor 重写了原来的constructor,因此会指向新对象,那个新对象没有指定构造函数,那么就默认为Object。

原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型。

function Person(){
}
Person.prototype={
    constructor:Person,
    name:'s',
    sayName:function(){
        console.log(this.name);

    }
}
var p1=new Person();
Person.prototype={
    constructor:Person,
    name:'h',
    sayName:function(){
        console.log(this.name);

    }
}
var p2=new Person();
p2.sayName()//h
p1.sayName()//s

原型优缺点:
它省略了构造函数传参初始化这一过程,带来的缺
点就是初始化的值都是一致的。而原型最大的缺点就是它最大的优点,那就是共享。

原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以。但如果属性包含引用类型,就存在一定的问题,一个实例改动,其他实例的该属性也跟着变了
解决:组合构造函数+原型模式

function Person(){
 this.name=['s','a'];
}
Person.prototype={
    constructor:Person,
    sayName:function(){
        console.log(this.name);

    }
}
var p1=new Person();
p1.name.push('d')
var p2=new Person();
p2.sayName()//['s','a'];p2的name没有随着改变
p1.sayName()// ["s", "a", "d"]

寄生构造函数,其实就是工厂模式+构造函数模式。这种模式比较通用,但不能确定对象关系,所以,在可以使用之前所说的模式时,不建议使用此模式。
在什么情况下使用寄生构造函数比较合适呢?假设要创建一个具有额外方法的引用类型。由于之前说明不建议直接String.prototype.addstring,可以通过寄生构造的方式添加。给js自带对象添加属性和方法

function myString(string) {
var str = new String(string);
str.addstring = function () {
return this + ',被添加了!';
};
return str;
}
var box = new myString('Lee'); //比直接在引用原型添加要繁琐好多
alert(box.addstring());

在一些安全的环境中,比如禁止使用this 和new,这里的this 是构造函数里不使用this,这里的new 是在外部实例化构造函数时不使用new。这种创建方式叫做稳妥构造函数。所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this 的对象。

function Box(name , age) {
var obj = new Object();
obj.run = function () {
return name + age + '运行中...'; //直接打印参数即可
};
return obj;
}
var box = Box('Lee', 100); //直接调用函数
alert(box.run());
//稳妥构造函数和寄生类似。

继承

利用原型链实现继承,所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。大家要记住,所有函数的默认原型都是Object 的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。

function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true

构造函数A,在构造函数A的原型中定义一些属性和方法。在定义一个构造函数B,让B的原型称为A的一个实例,那么B原型会指向A原型,B的实例会继承A原型中的属性和方法,。且指向B原型

构造函数、原型和实例的关系:每个构造函数(prototype属性指向原型函数)都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。

记住一点,实例指向构造函数的原型,实例和构造函数之间没有直接关系。

参考资料:JavaScript高级程序设计(第3版)

上一篇 下一篇

猜你喜欢

热点阅读