javascript高级程序设计读书笔记(五)

面向对象的程序设计

NO.1 对象

OO语言都有一个标志,就是都有类的概念,通过类可以创建多个具有相同属性的方法的对象。但JS里没有类的概念,所以它的对象也有所不同。JS把对象定义为无序属性的集合。

JS的每个对象都是基于一个引用类型创建的。

JS中有两种属性,数据属性和访问器属性;
1.数据属性:
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有四个描述其行为的特性:
Configurable:表示能否通过delete删除属性从而重新定义属性。
Enumerable: 表示能否通过for-in循环返回属性。
Writable: 表示能否修改属性的值。
Value: 包含这个属性的数据值。

这四个是对象的默认属性。要修改默认是属性的特性,可以用Object.defindProperty()方法.例如:把属性定义为只读,把属性定义为不能删除等。

var book = {
_year: 2004,
edtion: 1
}
_year是一种常见的记号,表示中能通过对象方法访问的属性。

创建对象:
虽然Object构造函数和字面的方法都可以用来创建单个对象,但是这些方法都有明显的缺点,就是当一个接口创建很多对象时,会有很多重复代码。为了解决这个问题,人们使用工厂模式的一种变体。

工厂模式抽象了创建对象的具体过程,例如:

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

工厂模式虽然解决了对个类似对象的问题,但是没有解决对象识别问题(怎么样知道一个对象的类型)。于是有了构造函数模式:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
}

var person1 = new Person("yzq", 25, "software engineer");

构造函数:
构造函数和其他的函数的唯一区别是调用的方式不同,构造函数都是通过new来调用的。

构造函数的问题:
构造函数虽然好用,但是在用构造函数创建对象时,当对象有方法时,就需要在每个构造函数里创建一个方法。为了解决这一个问题。将函数定义转移到构造函数的外面:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName
}
function sayName(){
        alert(this.name);
};

var person1 = new Person("yzq", 25, "software engineer");
        

原型模式:
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象包含由特定类型所有实例共享的属性和方法。
例子:
function Person(){};
Person.prototyoe.name = "yzq";
Person.prototyoe.age = 25;
Person.prototyoe.job = "cc"
Person.prototyoe.sayName = function(){
alert(this.name);
};

var person1 = new Person();
person1.sayName(); //"yzq"

var person2 = new Person();
person2.sayName(); //"yzq"
通过这种方式,将属性和方法共享。

理解原型对象:
1.只要一个新函数被创建时,就会根据一组的特定的规则为该函数创建一个prototype属性。
2.这个prototype属性是一个指针,指向函数的原型对象。
3.然后在默认情况下,原型对象都会自动获得一个构造函数属性。这个属性是指向prototype属性所在函数的指针。例如:Person.prototype.constructor指向Person
通过构造函数,可以继续为原型对象添加其他属性和方法。
4.在创建了自定义的构造函数之后,其原型对象默认只会取得constructor。其他方法,都是从Object继承而来
5.当用构造函数创建了一个新实例后,实例内部将包含一个指针为[[Prototype]]

个人理解:
Person.prototype指向原型对象,原型对象的构造函数Person.prototype.constructor又指回了Person。原型对象中除了包含构造函数属性之外,还有其他的属性。
而真正的每个实例都包含一个内部属性,数值只指向Person.prototype。实例和构造函数是没有任何直接联系的。

当代码读取某个对象的某个属性时,都会先从对象实例本身开始,如果在实例本身中找到了给定的名字的属性,则返回属性的值,如果找不到,则继续搜索指针指向的原型对象。例如:在调用person1.sayName()的时候,在person1中没有找到sayName属性,但是在person1的原型中有sayName属性。

另外,当对象实例可以访问原型中的属性,但是不能重写原型中的属性。所以当对象实例中有原型中的同名属性时,实例中的该同名属性会屏蔽原型里的属性。要想重新访问原型里的属性,可以用DELETE删除实例中的属性。

NO.2 原型和in操作符

有两种方式使用in操作符,单独使用和在for-in循环中使用。
单独使用时,in操作符会通过对象能够访问给定属性时,返回true。无论该属性存在实例还是原型中。

alert("name in person1")一直都是true,当属性存在时。不管在实例中还是原型对象里。

要想确定属性在对象中还是存在原型中,可以同时使用hasOwnProperty()和in操作符。方法如下:
function hasPrototypeProperty(object, name)[
return !object.hasOwnproperty(name) && (name in object);
}
hasOwnproperty只有在实例中才返回true。所以以上那个函数为true时,可以判断属性是在原型中。

要获取对象上所有可枚举的实例属性,可以使用Object.keys()方法。这个方法接受一个对象作为参数,返回一个所有属性的字符串数组

var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"

var p1 = new Person()
p1.name = "yzq"
p1.age = 25;
var p1keys = Object.keys(p1);
alert(p1keys); // "name,age"

更简单的原型语法:

function Person(){
}
Person.prototype = {
    name : "yzq",
    age : 25,
    sayName : function(){
        alert(this.name);
    }
}

注意:使用上面语法会导致constructor的属性等于Object而不等于Person了。
如果constructor真的很重要,可以自己定义。

function Person(){
}
Person.prototype = {
    constructor : Person,
    name : "yzq",
    age : 25,
    sayName : function(){
        alert(this.name);
    }
}

NO.3原型的动态性

先创建实例,在修改原型,指在原型里添加属性和方法。这样是OK的。

var friend = new Preson();
Person.prototype.sayHi = function(){
 alert("hi");
}
friend.sayHi(); //"hi"

但是先创建实例,在重新写整个原型。这样就会报错。

function Person(){
}
var friend = new Preson();
Person.prototype = {
    constructor : Person,
    name :"yzq",
    age :25,
    sayName: function(){
        alert(this.name);
    }
}

friend.sayName(); //error

NO.4原型模式的缺点

因为原型模式中的属性和方法都是共用的,所以,当一个实例修改原型模型中的属性时,其他实例中的属性也会改变。

所以原型模式和构造函数模式一般是组合使用:


NO.5 动态原型模式

为了解决同时写构造函数和原型这个问题,于是出现了动态原型模式。

function person(name,age,job){
    //属性
    this.name = name;
    this.age = age;
    this.job = job
    //方法
    if (typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    }
}

typeof this.sayName != "function"指的是当sayName方法不存在的时候,将方法放入原型中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容