关于 JS 的面向对象,好像有点明白了,又好像模糊。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
cc7756789
V2EX    Javascript

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

  •  
  •   cc7756789 2015-08-20 16:41:19 +08:00 4369 次点击
    这是一个创建于 3711 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我把一些 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 。

    16 条回复    2015-08-21 14:56:08 +08:00
    edire
        1
    edire  
       2015-08-20 16:54:09 +08:00
    被你说晕了。真心被你说晕了。我先回答你最后一个问题
    void1900
        2
    void1900   div class="badges">   2015-08-20 16:55:32 +08:00
    找不到对象
    edire
        3
    edire  
       2015-08-20 17:00:26 +08:00   1
    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
        4
    cc7756789  
    OP
       2015-08-20 17:02:05 +08:00
    o.run (New ); // Name is new name
    这里搞错了

    o.run.call (New ); // Name is new name
    cc7756789
        5
    cc7756789  
    OP
       2015-08-20 17:03:30 +08:00
    @edire 原来如此
    FrankFang128
        6
    FrankFang128  
       2015-08-20 17:03:53 +08:00
    看在用 Java 的角度理解 Javascript 我就明白了你为什么疑惑了。
    你这样是想不通的。
    请忘掉传统的 OOP ,进入 prototype 的世界。
    FrankFang128
        7
    FrankFang128  
       2015-08-20 17:04:54 +08:00   1
    this 不会丢失, this 永远指向 context , this 不会指向「当前对象」。
    Arrowing
        8
    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
        9
    sudoz  
       2015-08-20 17:07:49 +08:00
    @FrankFang128 是这么回事
    q84629462
        10
    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
        11
    edire  
       2015-08-20 17:08:42 +08:00
    然后 call o.run.call (New ) 实现了什么 实现了一个

    让 run 这个方法运行的时候作用域 this 指向了 New ,所以 那个地方的 this.name 也就是 "new name"
    WXYOO1
        12
    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
        13
    latelx  
       2015-08-20 18:51:12 +08:00
    你可以用 bind 把上下文绑定在方法上啊

    var run = o.run.bind (o )

    run ();
    YuJianrong
        14
    YuJianrong  
       2015-08-20 19:17:18 +08:00 via iPhone
    对 JS 语法有任何疑问去看语言 spec 是最简单的办法, es6 有点厚就算了,大部分特性去看薄薄的 es3 标志就好了。
    Rube
        15
    Rube  
       2015-08-21 11:09:02 +08:00
    js 作用域链
    bramblex
        16
    bramblex  
       2015-08-21 14:56:08 +08:00
    OwO 不明觉厉
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2625 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 12:50 PVG 20:50 LAX 05:50 JFK 08:50
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86