您的位置:首页 > Web前端 > JavaScript

JavaScript面向对象编程

2017-12-25 15:48 141 查看

创建对象

创建对象可以利用Object构造函数创建新对象

var stu =  new Object();


或者 用字面量去创建对象

var stu = {};


这两种方式存在问题是会产生大量重复代码

因此,有了工厂模式

工厂模式:其实就是定义一个函数,函数中声明了一个对象,这个对象的相关属性是由这个函数传进来的参数来进行初始化。最后将这个对象作为这个函数的返回值返回。

function Student(name, age){
var stu = new Object();
stu.name = name;
stu.age = age;
stu.hello = function(){
console.log("Hello,"+stu.name);
}
return stu;
}
var Jack = Student('Jack',22);


问题是,没有解决对象识别的问题,也就是说没办法知道一个对象是什么类型的。

因此,有了构造函数模式

构造函数模式:也是一个函数,但是这个函数与工厂模式不同是,在这个函数内部没有显示创建对象,直接把属性都赋给了this对象,而this指向将指向将要创建的对象。没有return语句。

function(name,age){
this.name = name;
this.age = age;
this.hello = function (){
console.log("Hello,"+this.name);
}
}
var Jack = Student('Jack',22);
var Max = Student('Max',23);


当用new关键字来调用这个构造函数时,先是创建了一个新对象,将构造函数的作用域赋给新对象(因此this就指向这个新对象),然后返回this所指向的这个对象。如果像普通函数来调用的话,与其他没有定义return的普通函数一样,返回的是undefined。相关属性和方法会挂在对应执行环境的全局对象上。在严格模式下,this指向的是undefined。

构造函数模式的问题在于,构造函数创建新对象的话,构造函数里的方法都会在每个新的对象上重新创建一次。可以验证利用构造函数创建的Jack和Max的hello方法不是同一个。如果单独把这些函数单独拎出来,放到成全局函数,在构造函数中再指向这个全局函数,虽然减少了重复的函数,但散失封装性。

因此,有了原型模式

在每创建一个新函数的时候,就会按照一定规则为其添加一个prototype属性,这个属性是一个指针,指向一个对象,这个对象用途是包含可以由特定类型的所有实例共享的属性和方法。这个原型对象和这个函数都是同时产生的。构造函数的prototype属性指向了构造函数对应的原型对象,这个原型对象的constructor属性指向构造函数。以这个对象为原型对象的对象继承了这个原型对象的的constructor属性,并且这些对象有一个属性[[prototype]](无法访问)或者proto (firefox、chrome、safari)指向的是原型对象。

function Student(){
}
Student.prototype.name = 'Jack';
Student.prototype.age = 23;
Student.prototype.hello = function (){
console.log("Hello, "+this.name);
}
/*上面做法赘余代码很多,更常见的做法如下*/
function Student(){
}

//原本在每创建一个函数的时候就会同时创建它prototype指向的对象
//而这个对象的constructor([[Enumerable]]默认为false)属性会默认指向这个函数

Student.prototype = { //这种做法相当于重写了Student的默认对象,其他结果是相同的,但是constructor不再指向Student
constructor:Student,//有需要的话需要重新设定constructor的指向([[Enumerable]]为true)
name:'Jack',
age:23,
hello:function(){
console.log("Hello, "+this.name);
}
}
//如果想要让constructor的[[Enumerable]]为false,可以用Object.defineProperty()
Object.defineProperty(Student.prototype,"constructor",{
enumerable:false,
value:Student
});


构造函数和实例对象之间没有直接的连接,这种连接是存在与实例与构造函数的原型对象之间。在访问对象的某个属性时,是从该对象开始沿着原型链向上查找,原型链的终点是null。示意图如下,是截自廖雪峰博客,感觉意思到了就懒得自己画了。



实例可以访问原型的属性和方法,但是不能改写。如果有重名的属性和方法,也只是在实例中创建同名属性或方法,并屏蔽原型中的。如果将实例中这个重名属性重新赋值为null,也不会更改实例的这个属性的指向重新回到原型对象,只用利用delete关键字才可以实现。

原型动态性:可以随时为原型添加属性或方法,这些属性或方法都能在实例中反映出来。

var Mike = new Student();
Student.prototype.bye = function(){
console.log("Bye");
}
Mike.bye(); //bye


但是如果是更改了整个原型的对象,情况就不一样了

function Student(){
}
var Lyn = new Student();
Student.prototype = {
sayHello:function(){
console.log("Hello");
}
}
Lyn.sayHello();//error,sayHello这个函数没有被定义


原因是Lyn这个实例的[[Protoyepe]]指向的是原来的原型对象,重写原型对象会切断现有原型对象与之前所创建实例之间的联系。

在理解原型的这些内容的时候,我觉得清楚每个部分的指向是很重要的。

原型存在的问题是,原型的所有属性和方法都被实例所共享,虽然对于一般属性值来说,可以在实例中定义同名属性去屏蔽原型中的属性,对于引用类型来说,所有实例都会用同一个引用类型。

function Student(){}
Student.prototype = {
constructor:Student,
name:'Lyn',
friends:['Tom','Jack']
}
var stu1 = new Student();
var stu2 = new
8c3a
Student();
var stu3 = new Student();
stu1.friends=['Rose'];
stu2.friends.push('Lily');

console.log(stu1.friends); //['Rose']
console.log(stu2.friends); //['Tom','Jack','Lily']
console.log(stu3.friends); //['Tom','Jack','Lily']


被广泛使用和认可的还是构造函数和原型混合,也就是利用构造函数模式定义实例属性,将实例的属性通过传参的方式进行定义,而原型模型用于定义方法和共享的属性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: