JavaScript JavaScript 的对象本质 (更适合有 java、c++、c# 等背景的)

xwf286 · 2014年02月11日 · 最后由 xwf286 回复于 2014年02月12日 · 5048 次阅读

javascript 是一个基于对象的语言。 1 如果单单从对象的角度审视 js 的话,她是那么优雅,所有的都是对象,对象就是一个 key-value 集合,对象是动态的,可以动态地增加属性和方法。 2 虽然,每个 js 对象都有一个隐含的到其原型的链接(这个连接是隐藏的,firefox 上可以利用proto看到,对象字面量的原型是 Object.prototype),每个 js 对象都会继承其原型的属性,虽然 javascript 的对象与基于类的语言完全不同,但仍然是很优雅的,不就是一个每个对象有一个原型吗(一直到最顶端)。 。。。。。

虽然理论上是这样的,但 js 代码中我们不能直接操作这个原型链,即不能 obj2=obj1.proto,如果可以这样的话,确实是够优雅的,但 js 没有这样做,这是 js 的第一个坑,我们称之为proto坑。请参考 java good parts 一书,作者描述为:插入了一个多余的中间层

3 js 不仅有对象,还有函数 function,除了函数之外居然还有一个 new,一个没有 class 的世界居然有 new,你让大伙怎么想? 接下来我们先看函数,再看 new。 函数初看很简单,就像 c 语言中的方法,由函数名、参数、函数体组成,主要起到封装、重用代码的作用, 但是,js 中的函数可不简单(如果是这么简单就 ok 了)。

函数也是对象,什么,函数也是对象?在 c 系语言中可不是这样的,函数(方法)是函数(方法),对象是对象,怎么函数也是对象呢? 函数确实是对象,是一种特殊的对象,我们知道,js 中每个对象都是一个键值对集合,还包含一个到其原型的链接,那么函数对象呢?函数对象的原型是 Function.prototype(Function.prototype 本身会连接到 Object.prototype),此外,函数对象还有两个特殊的隐藏的属性:函数的上下文和执行函数的代码。

每个函数有一个 prototype 属性(特别注意,这是函数对象特有的属性,不要和 js 中每个对象到其原型的连接相混淆,那个玩意是隐藏的,不过在 firefox 中你可以使用proto访问到),这个 prototype 属性是一个对象,姑且称之为 prototypeObject 吧,这个对象有个 constuctor 属性,指向函数本身,绕了一圈。

既然函数是对象,那么这个特殊的对象也可以像其他普通的对象那样,可以有属性、有方法,可以被当做参数在函数中传递,。。 这个对象的特殊之处就是"可以被调用"(注意,我在这里加上了括号),可以被调用的含义,就是遇到这类对象你可以直接在对象后面加上一对括号,以及若干参数。(非可调用对象可没有这样的特权)

回到proto坑上,我们不能直接指定一个对象的原型链,这样的话,怎么实现继承呢(你 javascript 说了,我可以直接实现基于对象的继承,又不提供手段,我怎么实现这个继承呢)?js 提供了一个足够绕 的途径,通过构造函数,也就是通过函数来实现继承,当你 new 一个函数时,这个 new 出来的对象的原型指向函数的 prototype 属性(我们说过,这个属性是一个对象),这样下来确实是够绕的了。

为什么这样做呢,不是直接操作原型链而是通过一个函数的方式呢?有人(不用说你也知道此人谁了吧)说,js 的发明者对基于原型的继承不太自信,硬生生地搞了个传统面向对象语言中的对象创建方式的模仿品,结果有点成了四不像!!

再论 this: 在 java 中,也有个 new,这个 new 是和类 class 搭配的,一起合作,创建一个以类为模板的新的对象,

混乱开始:但是 js 中的这个 new 干嘛用的?和谁搭配用

在 js 中,函数可以这样用(作为构造函数),即 new 一个函数。

怪就怪在 new 这个操作,new 的操作实际分两步: 1 创建一个空的对象 2 将新创建的空对象作为 this,调用该函数。

 function DogConstructor(name) {
    this.name = name;
    this.respondTo = function(name) {
        if(this.name == name) {
            alert("Woof");        
        }
    };
}
var spot = new DogConstructor("Spot");
spot.respondTo("Rover"); // nope
spot.respondTo("Spot"); // yeah!</div> 

上面的代码实际上就是:

// create an empty object
var spot = {}; 
// call the function as a method of the empty object
DogConstructor.call(spot, "Spot");

同时注意,函数里边有个 this,这个 this 是太动态了,this 指向的就是那个空的对象,这样的话,我们可以通过这种灵活性,可以以函数为模板,创建任意对象。

最后补充一点,js 设计成这样,我等平庸之辈只能瞻仰的份,对于 c 系程序员来说,这种语言的思维方式需要细细体会,(想想看,java 人渴望的一个 lamda 表达式历经多年还是那么难产),我们需要足够虔诚的态度拥抱这种迥异的语言。

另外,我想说的是,js 的动态性(她甚至可以修改 this)与函数性以及对象性是相辅相成,三者可以说缺一不可,再想想看,如果不能动态设置 this,函数作为一等公民是不可想象的。

欢迎大家吐槽!

内容还没看,吐嘈一下排版可以吗

没有什么代码,不要排了把?原谅我,还是不太熟悉 markdown

Python 的 self 是自动绑定的……刚开始弄 JS 这个乱跑的 this 简直坑死人啊……

#4 楼 @Kabie 这有点具有两面性,这一点也是与其他语言截然不同之处,我不太了解 python,至于你说的自动绑定,js 自己也是动态绑定啊,只不过自动绑定到函数的接受者对象上了。在一种情况下,特别是你想把一个对象 A 中的方法 method1(在 method1 中会引用 this 使用 A 的一些属性)提取出来作为一个函数对象在其他 context(也就是把其他对象 B 作为 method1 的接受者)下执行,js 会将 method1 中的 this 绑定到 B 上,这时候就会出错,这一点是需要注意的地方(特别是你在使用一些事件框架,例如 backbone,特别需要注意绑定的问题)。当然这是灵活性的代价

#5 楼 @xwf286 对……Python 里面一个对象的方法是已经绑定过 context 了的……JS 就得手动绑定一下……

……比如

['a','b','c].map(/a/.test)

这样是不行的……非要……

['a','b','c'].map(RegExp.prototype.test.bind(/a/))

这就跟写一个匿名函数差不多麻烦了……

functioin 就是下蛋的鸡,object 就是母鸡下的蛋,据说有个老母鸡 Function 也是从蛋孵出来的,到底是先有鸡还是先有蛋,这个不好说

楼主好几次把 Javascript 写成了 Java, 完全不是一回事哦:)

不要用其他语言的思想来理解 JS 有了这个前提,一切都好说。

想问一下:招前端偏 JS 职位,问这个问题(javascript 对象)合适吗?

求摘要

alert(Function instanceof Object); alert(Object instanceof Function);

楼主调下排版吧,不然简直无法阅读。

#8 楼 @billy 我的这番话应该对于大部分人来说,如果你接触过的语言只有 js(没有贬义)(没有接触过 c、c++、java、c#、python、ruby、面向对象分析、四人帮设计模式、uml、重构等),那么恭喜你,就不会纠结了。

#9 楼 @frank_128 说法是对的,但是很难吧(特别是对于许多专职后端要转向 node 的人来说),与其他的面向对象开发语言来说。js 的面向对象绝对独树一帜,想象一下 c、c++、java、c#、python、ruby、面向对象分析、四人帮设计模式、uml、软件工程。。。

那个好心人帮我排版的,谢谢啦!前后都加上 ```,我记住了

需要 登录 后方可回复, 如果你还没有账号请 注册新账号