关于 JS 的面向对象,好像有点明白了,又好像模糊。

2015-08-20 16:41:19 +08:00
 cc7756789

我把一些 DOM 方法赋给变量,以供方便使用,但是遇到了错误。

var gTag = document.getElementsByTagName;
// undefined

gTag
// getElementsByTagName ()

gTag ("p")
// TypeError: 'getElementsByTagName' called on an object that does not implement interface Document.

刚接触 JS 没多久,非常不理解,为什么把这个方法赋给变量,不用括号输入变量名就是直接调用了。
然后我了解了下 call 和 apply 方法,原来 JS 下有这种好用的机制,一个新对象未定义一个方法,可直接调用旧对象的方法,只需要把新对象作为 this 传给旧对象的方法。

function Old (){}

Old.prototype = {
    name: "old name",
    run: function (){
        console.log ("Name is: " + this.name );
    }
}

var o = new Old;

var New = {
    name: "new name"
};

o.run (New ); // Name is new name


var run = o.run;
run.call (o );  // name is old name

但是我非常不理解为什么把实例的方法赋值给一个变量后会丢失 this 指针。

这里无意挑起语言的纷争!!

然后我想起了 Python 的类:

class A:
    def run (self ):
        return "running"

a = A ()
run = a.run  
run ()

A.run ()
"""Traceback (most recent call last ):
File "<stdin>", line 1, in <module>
TypeError: unbound method run () must be called with A instance as first argument (got nothing instead )"""

A.run (a )

好像明白了些什么,但是又好像模糊!

JS 里面把实例方法赋值给变量,以变量再调用,但是 this 会丢失。就好像如 Python 中的 A.run
未实例化直接使用,也会报错。 JS 在 nodejs 环境下虽然未报错,但是输出的是 Name is undefined 也就是说找不到 this.name ,当成一个普通函数运行了。

我的理解是否有误?

现在还是不太明白为什么对实例方法赋值操作 var a = object.method 会丢失 this 。

3713 次点击
所在节点    JavaScript
16 条回复
edire
2015-08-20 16:54:09 +08:00
被你说晕了。真心被你说晕了。我先回答你最后一个问题
void1900
2015-08-20 16:55:32 +08:00
找不到对象
edire
2015-08-20 17:00:26 +08:00
var a = object.method 会丢失 this 。

不是会丢失 this 。是这样的你看这个

var object = {
name: 'username',
method: function () {
console.log (this.name );
}
};

我如果执行 object.method (); 这样的话 他就会输出 username
如果我先赋值给 a;

var a = object.method

那么,其实等于

var a = function () {
this.name;
}

当前的 this 指向的是 window ,也就是 window.name
cc7756789
2015-08-20 17:02:05 +08:00
o.run (New ); // Name is new name
这里搞错了

o.run.call (New ); // Name is new name
cc7756789
2015-08-20 17:03:30 +08:00
@edire 原来如此
FrankFang128
2015-08-20 17:03:53 +08:00
看在用 Java 的角度理解 JavaScript 我就明白了你为什么疑惑了。
你这样是想不通的。
请忘掉传统的 OOP ,进入 prototype 的世界。
FrankFang128
2015-08-20 17:04:54 +08:00
this 不会丢失, this 永远指向 context , this 不会指向「当前对象」。
Arrowing
2015-08-20 17:05:03 +08:00
var gTag = document.getElementsByTagName;
这一句只是将 getElementsByTagName 这个方法引用赋值给 gTag
而 js 里是方法执行时确定上下文对象的,当你执行这个方法时,会查找上下文对象,而这里明显缺乏一个上下文对象

错误提示是 getElementsByTagName 的上下文对象必须是 document 或集成 document 的
你可以这样:

var gTag = function (tagName ){
return document.getElementsByTagName (tagName );
};
gTag ('p');

或者这样:

var gTag = document.getElementsByTagName.bind (document );
gTag ('p');
sudoz
2015-08-20 17:07:49 +08:00
@FrankFang128 是这么回事
q84629462
2015-08-20 17:08:24 +08:00
[var a = object.method 会丢失 this ]
并不是丢失了 this :

var a = {a:function (){console.log (this );}};
var b = a.a;
a.a ();
b ();

this 一直都在,只是这个 function 的运行环境改变了
edire
2015-08-20 17:08:42 +08:00
然后 call o.run.call (New ) 实现了什么 实现了一个

让 run 这个方法运行的时候作用域 this 指向了 New ,所以 那个地方的 this.name 也就是 "new name"
WXYOO1
2015-08-20 18:06:59 +08:00
问题一:像 @Arrowing 所说的, Js 自带的方法要考虑上下文, document.getElementsByTagName 必须绑在全局上。
另外扩展 Js 本身的一些方法一般是重新定义一下:比如像一些 ie 兼容的扩展方法等
var gTag = function (tagName ){
return document.getElementsByTagName (tagName );
};
gTag ('p');


问题二:两个关注点:
1.prototype 和 call\apply
function Old (){}

Old.prototype = {
name: "old name",
run: function (){
console.log ("Name is: " + this.name );
}
}

var o = new Old;

var New = {
name: "new name"
};

o.run (New ); // Name is (old old old )/3 遍 name
//o.run 并没有接受参数的地方,你应该要写成 o.run.call (New );吧?
//1. o.run (New ); 打印 old name 的原因是 o.run o 从类 Old 定义而来,而 Old 的 prototype.name = “ old name ”;
//2. 若你本意是 o.run.call (New ) 则打印 Name is new name 此处指针会指向 New 对象。
latelx
2015-08-20 18:51:12 +08:00
你可以用 bind 把上下文绑定在方法上啊

var run = o.run.bind (o )

run ();
YuJianrong
2015-08-20 19:17:18 +08:00
对 JS 语法有任何疑问去看语言 spec 是最简单的办法, es6 有点厚就算了,大部分特性去看薄薄的 es3 标志就好了。
Rube
2015-08-21 11:09:02 +08:00
js 作用域链
bramblex
2015-08-21 14:56:08 +08:00
OwO 不明觉厉

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/214598

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX