JavaScript中去掉原型模式中this的疑问??

function Person(name){
   this.name=name;
}
Person.prototype.getName=function(){
return this.name;
}
//调用函数
var person=new Person("Nicholas");
alert(person.getName());

疑问:
同一个函数,为啥在去掉this(原型模式中的this)后代码如下:

Person.prototype.getName=function(){
    return name;
}

出现如下的错误:

Test3.html:102 Uncaught ReferenceError: age is not defined

this不去掉的话,就会输出Nicholas;

阅读 2.6k
2 个回答

原型和构造函数中的this都指向new出来的对象,构造函数中的代码是为name赋值,而原型中的代码是访问name的值,去掉this就是在全局环境中找name了,找不到所以返回undefined。

不要把原型链和变量作用域搞混了,前者是为了实现对象的继承,后者方便的解释了变量的查找规则。

name 的意思是查找变量name
this.name 的意思是查找对象thisname 属性
这两者的意思是不一样的,前者按照变量作用域的规则进行查找,后者仅仅是访问某个对象的一个属性。

1、 变量作用域(ES5.1之前,先理解简单的,也先忽略某些闭包)

在JS里面只有一种作用域,那就函数作用域。
变量的查找就是看你代码中的 {}(专指函数定义中的{}),每次函数定义会形成一级作用域。在某个作用域内使用var定义的变量就是定义在该作用域中的变量。

// 全局作用域
var a=1; // 定义在全局的变量
function fn ()
{ // fn 形成的作用域
    var b=2;        // 定义在 fn作用域中的变量
    console.log(a);
    function fn2()
    { // fn2 形成的作用域
       var c;       // 定义在 fn2 作用域中的变量
       var a = 0;  // 定义在 fn2 作用域中的变量
       return a+b;
    }
    return fn2;
}
var d; // 定义在全局的变量
var obj={};
obj.a = fn;
obj.b = fn(); // 即 obj.b = fn2; 

当写下一个变量,首先在本作用域中查找是否有该变量,否则递归的向上一级作用域中查找。比如return a+b中的a会在本作用域中查找到一个定义在本作用域中的变量,故a0,但是b却本作用域中查找不到,所以去上一级去查找,即fn形成的作用域,故b2。在console.log(a)依旧是这样,所以a1。查找不到的就会抛出异常。

由于此种规则,所以不论是obj.a()还是fn()输出都是0,同样obj.b()返回2

2、对象属性的查找规则

当写下 obj.name(获取其值)的时候,就是按照对象的属性查找规则查找。

var name = "His name is "
function Person(name)
{ this.name = name; }
Person.prototype.getName = { return name + this.name;}
var p = new Person("n");
p.say = function (){ return "hello!";}

当执行var p = new Person("n"); 的时候,其实解释器暗暗的做了一件事情p.__proto__ = Person.prototype。当访问p.getName,解释器首先查找在p中有没有属性getName,否则递归的去 __proto__中查找这个属性。所以会找到Person.prototype中定义的那个函数(如果最终找不到则返回undefine,这个跟变量查找不到是不同的)。同样p.say()返回的会是"hello!"

3、this 的值

在JS this其实是一个关键字,不是变量。因为不是变量所以其查找规则是另外的一套。下面只说一下与本问题有关的(记住就好没有为什么)。其余的参考这里
(1) var p = new Person("n")的时候,其实类似与如下代码(仅仅作为说明)。

{   // 解释器的处理
    var obj = {},name="n";
    obj.__proto__ = Person.prototype;
    this = obj;
    // Person中的代码
    this.name = name;
    p = this;
}

this是一个新创建的对象,并且最终会付给 p,所以对象p 是含有属性name的。

(2) p.getName() 的时候。在执行函数getName 的时候,其里面的 thisp
所以此时的this.name 其实就是p.name

4、 结论

所以return name + this.name,中name按照变量的查找规则,结果是 “His name is ”this.name就是p.name"n"
至于为什么只是抛出age未定义,是因为在浏览器里面在全局定义了一个变量name其值为空字符串。如果只是测试的话建议换一个变量名。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题