Javascript高级程序设计 读书笔记(三)

今天中秋假期的第二天,继续开始学习这本书咯~

第6章:面向对象的程序设计

constructor,prototype属性是不可枚举的

创建对象

工厂模式

function createPerson(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayHello = function(){
        alert(this.name);   
    }
    return o;
}

var person1 = createPerson("Daming",12);
var person2 = createPerson("Xiaohong",11);

特点:

  1. 工厂函数内部会每次生成一个新的Object对象。
  2. 函数结束会返回新建立的对象。
  3. 缺点:无法识别这个新生成对象的类型。

构造函数模式

function Person(name,age){  
    this.name = name;
    this.age = age;
    this.sayName = function(){
        alert(this.name);
    }
}
var p1 = new Person("Daming",12);
var p2 = new Person("Xiaohong",11);

特点:

  1. 构造函数内没有显式创建新对象。
  2. 通过new来生成对象。不过可以不用new。只不过这个时候,由于在函数内部this指向的是window,故实际上是添加了全局变量。
  3. 没有return值。
  4. 缺点:每个实例都会重新创建一遍每个方法。意思就是每生成一次实例,都会再编译一遍他的方法。而且每个实例的同名函数是不相等的,比如:
alert(p1.sayName == p2.sayName);//false

为了解决这个问题,可以通过将函数定义在外部。

function Person(name,age){  
    this.name = name;
    this.age = age;
    this.sayName = sayName;
}
function sayName(){
    alert(this.name);
}
var p1 = new Person("Daming",12);
var p2 = new Person("Xiaohong",11);

但是这样的话是不是就违反了封装函数的要求呢

原型模式

每个函数都有一个prototype属性。就是一个指针,指向一个大容器,包含这个类型的所有实例共享的方法和属性。这样就可以使得实例共享一些数据。

function Person(){
}
Person.prototype.age = 15;
Person.prototype.sayName = function(){alert(this.name);}

var person1 = new Person("Daming");
var person2 = new Person("Xiaohong");
person1.name = "Daming";
person2.name = "Xiaohong";

从上面的例子来看。Person内有prototype这个指针,指向一个大容器(我们暂时叫做PersonProto,随便叫的),PersonProto内包含constructor(这个是自动就有的,指向Person),还有age。

注意Person.prototype->PersonProto,PersonProto.constructor->Person。实际上构成了一个循环了,还蛮有意思的。

每一个实例中也有一个[[Prototype]]指向PersonProto。但是正常情况下是没法访问到这个属性的。在ES5中可以通过Object.getPrototypeOf() 来获取这个属性。
如果不是在原型链中的属性,就不会被所有对象进行共享。比如上述代码中的name属性。
简单介绍了一下原型链,我们可以来说一下具体读到某个属性的发生的事。
每当代码读取某个对象的某个属性时,都会进行一次搜索。首先查这个对象实例有没有这个属性。没有的话再查原型链。
故如果在实例中修改属性值,是会在实例中新创建一个属性,并不会修改原型链中的数据,也就不会影响其他实例的值。

function Person(){
}
Person.prototype.age = 15;
Person.prototype.sayAge = function(){alert(this.age);}

var person1 = new Person();
var person2 = new Person();
person1.name = "Daming";
person2.name = "Xiaohong";

person1.age = 13;
person1.sayAge();//13;
person2.sayAge();//15
//person1中的age=15并不会被13替代,只是存在于原型链中,可以通过删除实例的属性来恢复原型链属性
delete person1.age;
person1.sayAge();//15
关于判断属性是属于原型链还是实例中

思路很简单,hasOwnProperty()可以判断属性是否存在实例中。in可以判断是否有这个属性(不管是原型链还是实例),两个方法结合起来就OK啦

function Person(){
}
Person.prototype.age = 15;
Person.prototype.sayName = function(){alert(this.name);}

var person1 = new Person();
var person2 = new Person();
person1.name = "David";
person2.name = "Xiaohong";
function hasPrototypeProperty(obj,name){
    return !obj.hasOwnProperty()&&(name in obj);//存在于对象但是不存在于实例中
}
获取属性方法
  • 获取所有可枚举的实例属性(prototype默认不可枚举):ES5中有Object.keys()方法可以接受一个对象作为参数,返回所有属性的字符串数组。
  • 获取所有实例属性(包括prototype):Object.getOwnPropertyNames()。

更简单的原型语法

function Person(){
}
Person.prototype = {
    name:"David",
    age:15,
}//实际上是用一个字面量重新写了Person的prototype
var person = new Person();

但是这样写了会出现以下结果

alert(Person.constructor == Person);//false

原因在于原本prototype是会默认加上constructor属性,并指向Person。但是这样写的话就相当于把原型链重写了一遍。为了解决这个问题,可以手动加上

Person.prototype = {
    name:"David",
    age:15,
    constructor:Person
}

另外要注意的是这样写会破坏原型的动态性。看以下代码

function Person(){}

var person1 = new Person();
Person.prototype = {
    name:"David";
    sayName: function(){
        alert(this.name);
    }
}
person1.sayName();//error

原因是在实例化的时候的Person已经和之后的Person不一样了。

特点:

  1. 每个实例都可以共享一些属性和方法
  2. 也可以灵活的为每一个实例添加一些属性,不会影响到原型链
  3. 可以动态的添加方法,并且每个实例都可以用到。
  4. 缺点:想要每个实例都能在生成的时候都有一个引用属性的话,就会出现问题。
function Person(){
}
Person.prototype = {
    constructor:Person,
    name:"Daming",
    age:12,
    friends:["Xiaohong","Liwei"]//引用属性
}
var person1 = new Person();
var person2 = new Person();

person1.friends.push("haha");
alert(person2.friends);//Xiaohong,liwei,haha

可以看到我们没办法像普通属性那样去屏蔽原型链的内容,而是直接修改原型链。因此我们一般采用构造函数和原型模式组合使用

function Person(name,age){
    this.name = name;
    this.age = age;
    this.friends = ["Xiaohong","Liwei"];
}
Person.prototype={
    constructor:Person,
    sayName:function(){alert(this.name);}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值