JavaScript神奇的面向对象特性(上)

本文写作时长5小时

OO语言都有一个特点,就是都存在类的概念。通过类自定义类型,创建对象实例。和这些OO语言不同,JavaScript中是没有类的,又或者说原型对象就是JavaScript中的类。但是没有类的JavaScript是如何创建对象呢?退一步讲,在JavaScript中,对象到底是什么?

理解对象的本质

ECMA中关于对象的定义

对象就是无序属性的集合,属性的值可以是基本值,函数或者其他对象

通过JavaScript提供的Ojbect()函数,可以创建出一个简单的对象实例

var person = new Object();
person.name = "fuckJapan";
person.age = 21;
person.sayHello = function () {
    alert(this.name)
}

创建了一个person对象,person有三个属性,其中nameage是基本值,sayHello是一个函数。

这种写法可以用字面值对象重写为:

var person = {
    name:"fuckJapan",
    age:21,
    sayHello:function () {
        alert(this.name)
    }
};

这种语法看起来更加 封装

上面两种创建对象的方式,有点像手工作坊,每创建一次对象都要重新给对象添加属性,赋值,不胜其烦。怎么解决?进工厂,上生产线。

工厂模式

工厂模式抽象出创建对象的工程。既然JavaScript中没有类,所以就用函数代替,类似这样:

function createPerson(name, age, job) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.job = job;
    obj.sayHello = function () {
        alert(this.name);
    }
    return obj;
}
var person1 = createPerson("fuckJapan1",21,"student");
var person2 = createPerson("fuckJapan2",22,"teacher");

有了生产线,工人们再也不用亲自动手了,直接把原料放入函数,duang!一个person,两个person,统统new出来。工人解放了双手,提高了生产力,效率蹭蹭上升。

我是谁,我来自哪里?

虽然工厂模式解决了创建多个类似对象的问题,但是却没有解决对象识别的问题。怎么办?

构造函数模式

构造函数和普通的函数没有任何区别,只是用new调用时会产生特殊的效果。另外构造函数有一些约定的规定,比如首字母大写。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayHello = function () {
        alert(this.name);
    }
}
var person1 = new Person("fuckJapan1",21,"student");
var person2 = new Person("fuckJapan2",22,"teacher");

这种模式中,函数中并没有显示创建对象,而是使用了 this 关键字给对象实例添加属性。在构造函数的结尾也没有return。在调用时,使用了new关键字。在用new调用构造函数时,一共做了这么几件事:

  • 分配对象内存,创建新对象
  • 将这个对象和this绑定,this指向这个对象
  • 执行构造过程并返回对象(在构造函数末尾返回任何东西都不会起作用)

为什么这种模式解决了对象识别的问题呢?别着急,这是因为在person1person2中还包含一个名为constructor的属性,这个属性指向构造函数Person。从此person1person2不再是三无产品,是可溯源的正品,它们是由一个名为Person的构造工厂生产的。

alert(person1.constructor == Person)//true
alert(person2.constructor == Person)//true
alert(person1.constructor == person2.constructor)//true

alert(person1 instanceof Person)//true
alert(person2 instanceof Person)//true
alert(person1 instanceof Object)//true
alert(person2 instanceof Object)//true

person1person2为什么都是Object类型,这是因为在JavaScript中所有的对象都有一个共同的祖先Object

不走寻常路,把构造函数当做普通函数调用

Person("fuckJapan",23,"student");
alert(window.name);//fuckJapan

由于如果在浏览器中执行,则构造函数中的属性被添加到window对象中

指定作用域

var obj = new Object();
Person.call(obj,"fuckJapanAgain",25,"student");
alert(obj.name);//fuckJapanAgain
alert(obj instanceof Person)//false

这种偷鸡摸狗的调用方法,就会导致生产出来的对象没有厂家

构造函数虽然解决了对象的识别问题,但是却没有解决对象方法的重复创建问题,person1person2中的sayHello方法是两个完全不同的方法,这是完全没有必要的

alert(person1.sayHello == person2.sayHello)//false

补救措施,将方法拿到外面,指向全局函数

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

function sayHello() {
    alert(this.name);
}

暂时解决了方法重复创建的问题,可是,把自己的方法拿到外面这种方式总是不妥的,全局作用域中的函数却是特定对象的方法,这太诡异了。而且方法少了还好说,多了就太乱了。

原型模式

每个函数都有一个名为prototype的属性,指向函数的原型对象,类似OC中的类对象。原型对象中保存着创建对象实例共享的方法和属性。

function Person() {
}
Person.prototype.name = "fuckJapan";
Person.prototype.age = 29;
Person.prototype.job = "student";
Person.prototype.sons = ["son1","son2"];
Person.prototype.sayName = function () {
    alert(this.name);
}
var person1 = new Person();
var person2 = new Person();
alert(person1.name);//fuckJapan
alert(person2.name);//fuckJapan
alert(person1.sayName == person2.sayName);//true

构造函数变成了空函数,原型对象可以看做是对象的模板,所有对象生成时,默认都有这些属性和对应的值,其中引用类型是所有对象共享,非引用类型都是独立的存储。这样虽然解决了每个对象实例方法重新创建的问题,但又带来了新的问题,数据紊乱,其实不光这样一个问题,还有创建出的对象属性还需要一个个赋值,构造函数根本没什么用

person1.name = "fuckJapanAgain";
alert(person1.name);//fuckJapanAgain
alert(person2.name);//fuckJapan

基本类型的并没有受影响

person1.sons.push("son3");
alert(person1.sons);//son1,son2,son3
alert(person2.sons);//son1,son2,son3

由于引用类型是所有对象共享,所以数据乱了

说了这么多了,这个也有问题,那个也有问题,那么到底该怎么做呢?

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

在构造函数中创建每个对象需要单独使用的属性,比如sons。在原型中创建所有对象共享的属性,比如sayHello

对象实例中在构造函数中创建的属性,每个都保留单独的副本,而在原型中创建的引用类型属性都共享同一份引用。同时这种模式还支持传递参数,可谓一举两得。

function Person(name,age,job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sons = ["son1","son2"];
}

Person.prototype = {
    constructor:Person,
    sayHello:function () {
        alert(this.name);
    }
}

var person1 = new Person("fuckJapan1",21,"student");
var person2 = new Person("fuckJapan2",26,"teacher");

person1.sons.push("son3");
alert(person1.sons);//son1,son2,son3
alert(person2.sons);//son1,son2

alert(person1.sayHello == person2.sayHello);//true

所有问题差不多都几乎是完美解决,这就是JavaScript中神奇的『类』。

除此之外,还有动态原型模式寄生构造函数模式稳妥构造函数模式。读者自己去研究。

说到面向对象,必须要说的就是继承,JavaScript中的「类」已经很神奇了,作为面向对象特性之一的继承则更加神奇,各种花式继承干到你哭。

欲知继承如何,且听下回分解...

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

推荐阅读更多精彩内容