使用新的操作员 - 来自John Resig#36

时间:2011-03-18 00:55:37

标签: javascript

下面的示例代码是John Resig的Learning Advnaced JavaScript中的#36。 http://ejohn.org/apps/learn/#36

  

It is called We need to make sure the new operator is always used.

六个问题 - 我希望尽可能多地提供详细信息

1)此代码中是否实际调用了function User?我注意到,当它显示assert(user...)时,用户是小写的。如果函数被调用,怎么样?当它断言变量user时会调用它,该变量用户附加了一个函数调用,即User("John," name)

2)如果我m correct in assuming that function User is never called, is there a way that the code this.name = first +“”+ last;`运行?

3)如果调用了User函数,或者调用了它,可以在函数User中解释操作的顺序。例如,它返回新用户this.name = first + " " + last;如果调用或调用此函数,它将如何工作?

4)!(this instanceof User)如果是真的,以什么方式。由于函数User是对象,不会“这个”总是它自己的一个实例吗?

5)关于第一个断言,即assert(user, "this was defined correctly, even if it was by mistake"),你能否解释一下它是如何被正确定义的,重要的是,请解释一下这是怎么一个错误?它应该如何完成所以它不是一个错误?

6)关于第二个断言,为什么值得注意的是保留了正确的名称? ISN t it as simple as variable名为having been assigned Resig`。您可能以何种方式改变名称?

function User(first, last){ 
  if ( !(this instanceof User) ) 
    return new User(first, last); 

  this.name = first + " " + last; 
} 

var name = "Resig"; 
var user = User("John", name); 

assert( user, "This was defined correctly, even if it was by mistake." ); 
assert( name == "Resig", "The right name was maintained." );

4 个答案:

答案 0 :(得分:3)

此示例演示了JavaScript设计中的一个基本缺陷,即调用函数用作没有new运算符的构造函数可能会导致无意中修改全局对象。

    在此行调用
  1. Uservar user = User("John", name);。小写user包含对新的大写User实例的引用。

  2. 见#1。

  3. 如果在没有User的情况下调用new,则this将不会是instanceof User。发生这种情况时,我们会立即致电new User(...)。在第二次调用中,this 一个instanceof User,因此跳过条件块,我们继续使用构造函数,创建一个新实例User最初的意图。

  4. 在没有new运算符的情况下调用它时,函数中this的值只是引用全局对象,即浏览器中的window。这是JavaScript设计中的一个严重错误,本练习演示了一种解决方法。

  5. 因为User构造函数没有明确的返回(没有instanceof检查的阻止),如果在没有User的情况下调用new,它将返回{ {1}} undefined会失败。 assert检查会阻止此操作。

  6. 如果没有instanceof检查的保护,则在没有instanceof的情况下调用User会导致意外结果。由于new会引用this,因此window的分配将更新this.name,而不会更新新window.name实例的name属性,并且User会失败。

答案 1 :(得分:3)

任何给定的函数对象都可以使用两种方式。

  1. 作为一项功能
  2. 作为构造函数
  3. 差异是this绑定的值。当您使用函数作为构造函数时,this绑定将是从构造函数的公共原型继承的新对象。通常调用函数会将this绑定设置为全局对象。 (在ES5中使用严格模式,这不会发生。)

    第二个断言非常重要,因为您知道this绑定可以强制转换为全局对象。


    简化调用构造函数

    function ConstructUser(fname, lname) {
        var obj = {}; // Empty Object
    
        // In reality a special internal Prototype property
        // is assigned rather than 'obj.prototype'.
        if (User.prototype instanceof Object) {
            obj.prototype = User.prototype;
        } else {
            obj.prototype = Object.prototype;    
        }
    
        // Now call the User function with the 
        // new object as the 'this' binding.
        User.call(obj, fname, lname);
    
        return obj;
    }
    

答案 2 :(得分:2)

  

1)是否在此代码中实际调用过用户函数?我注意到当它说assert(用户...)时,用户是小写的。如果函数被调用,怎么样?当它断言变量user时会调用它,该变量user附加了一个函数调用User(“John,”name)

是。首先调用绑定到全局对象的this(请参阅下面的警告),然后使用空白对象调用自身,其原型为User.prototype绑定到this

第一个“call”调用内置函数调用操作符,第二个“call”调用内置函数构造操作符。对于用户定义的函数,两者是相同的,但规范确实区分了两者。

  

2)如果我认为函数User从未被调用是正确的,那么有没有办法运行代码this.name = first + " " + last;

是。第二次调用将达到,因为在第二次调用中thisUser

  

3)如果调用了User函数,或者调用了它,可以在函数User中解释操作的顺序。例如,它在执行此操作之前返回新用户.name = first +“”+ last;如果调用或调用此函数,它将如何工作?

在身体完成之前,呼叫不会返回。所以“调用”调用开始,它启动“构造”调用,返回,产生新的User,然后由第一次调用返回。

  

4)以什么方式可以!(这个用户实例)如果是真的。由于函数User是对象,不会“这个”总是它自己的一个实例吗?

没有。如果if语句不存在,User.call({}, "John", "Doe")会导致User被“调用”,this绑定到空白对象。尝试运行

var notAUser = {};
User.call(notAUser, "John", "Doe")
alert(JSON.stringify(notAUser));

你应该

{ "name": "John Doe" }
  

5)关于第一个断言,即断言(用户,“这是正确定义的,即使它是错误的”),你能解释一下它是如何被正确定义的,更重要的是,请解释它是怎么一个错误?它应该如何完成所以它不是一个错误?

作者认为,在new之前放User是程序员错误。

  

6)关于第二个断言,为什么值得注意的是保留了正确的名称?它不像变量名称一样简单,已经被赋予了.Resig`。您可能以何种方式改变名称?

全局变量name是全局对象的属性。如果this绑定到全局对象,则分配this.name将更改全局变量。

警告:

语言律师, 根据语言规范,当您将函数作为函数调用而不是作为方法时,this绑定到null。规范的另一部分说当你阅读this时,如果它是null,你得到的是全局对象。这种差异在EcmaScript 5严格模式的情况下很重要,因为这会改变第二部分,因此在阅读this时不会有任何有趣的东西。如果this为空,则读取它会返回值null

编辑:

构造运算符仅在JavaScript规范中定义。 JavaScript语言中的new运算符就是您调用它的方式。

对于用户定义的函数,它的工作方式如下:

  1. 代码调用new f(arg1, arg2)
  2. JS解释器为新对象分配空间并将其prototype设置为f.prototype
  3. JS解释器调用fthis设置为新对象,arg1arg2作为实际参数。
  4. 如果f返回一个对象,则会将其用作new的结果。
  5. 否则,使用新对象。如果返回任何非对象值,甚至是字符串或数字,则新对象是new的结果。

答案 3 :(得分:1)

1.在 var user = User(“John”,name);

中调用用户函数

2.pass

3.我不明白你的问题。

4

  

user = User(“a”,“b”);   会这样做。

5.user = new User(“a”,“b”);是正确的。

6.我不知道。