var与this定义变量的区别以及疑惑
发布于 12 年前 作者 xlqstar 18128 次浏览 最后一次编辑是 8 年前

我们知道: var可以定义一个局部变量,当然如果var定义在最外层的话,就是全局的局部变量,也就算是全局变量了。 而this关键字定义的变量准确的说应该算是成员变量。即定义的是调用对象的成员变量。 另外在“类(构造函数)”中,我们通常也会用var定义私有属性,而this定义公共属性。 以上这些是大家都知道的,若不知道看了就大概知道的。但我今天遇到了一个疑惑,恳请社区高手指点。是这样的:

//打开浏览器控制台执行一下命令
//定义了一个全局变量
var wahaha = “娃哈哈”;//这里双引号改成英文双引号

//输出“娃哈哈”
console.log(wahaha);

//输出“娃哈哈”,我们知道这里的this指向的是window,即之前的var定义已经把wahaha变量定义成了window的成员变量
console.log(this.wahaha);

上面代码两条控制台日志语句都输出了“娃哈哈”的结果,然而如果这样

//定义一个类
function ClassA(){

  //定义一个私有属性
  var wahaha = “娃哈哈”;//这里双引号改成英文双引号

  //输出“娃哈哈”;
  console.log(wahaha);

  //输出undefined
  console.log(this.wahaha);
}

var a = new ClassA();

这里的输出结果就不同了 ,console.log(wahaha)顺利输出“玩哈哈”,而console.log(this.wahaha);就输出了“undefined”。其实我是觉得这个示例跟之前的那个示例的效果是有矛盾的地方的。我明白很多人利用这个特性实现私有变量跟公共变量的效果,但真的不得不说这是很矛盾的并且令人费解的。

矛盾点在于:第一个例子,var定义的全局变量会很顺利的成为window的成员变量,直接用“变量名”或者“this.变量名”都能得到结果,而第二个例子中,在构造函数中用var定义变量的话,该变量却只能在内部直接通过“变量名”获得而无法通过“this.变量名”的方式获得。

不知道我的问题描述的够不够清楚,这是今天琢磨《javascript精粹》时候想不通的一个问题。恳请高手指点

24 回复

this指的不是ClassA,而是a

var是在当前作用域(scope)中声明一个变量,而this则是指向当前上下文(context)。 作用域很好理解,在函数里面,作用域就是执行var语句的那个函数,否则就是root(window或者global)。 上下文是在函数调用的时候决定的:

foo.bar() // 上下文是foo(this === foo)
foo['bar']() // 上下文是root
foo['bar'].apply(obj) // 上下文是obj
foo['bar'].call(obj) // 上下文是obj

大哥,如果你在浏览器上也写这个函数ClassA,效果是一样的啊,你在浏览器上定义的var wahaha,因为没有嵌套在函数中,所以他指向全局对象windows,而如果你在函数里面定义,this当然指向这个函数咯,看来这些要多看看咯,推荐看看犀牛。

非常感谢您的回复,但请看清楚我的问题

这貌似跟我的疑惑没有关系啊?

记得以前在推上抱怨 JS 循环添加事件太恶心的时候, 人家就安慰我说, JS 的 this 本来就乱指的…

@xlqstar 没有关系?哈哈,看看作用域和函数吧,再看看函数作用域.

不能说乱指吧,反正规律不好找,我就把我遇到的这个特殊情况背下来得了,理解不了

@xlqstar 我已经对 JS 保持失望了

不会别的,不懂专业术语.不过听说js和别的高级面向对象的好像有些不同.

`var wahaha = ‘爽歪歪’ ; function ClassA(){

//定义一个私有属性 var wahaha = “娃哈哈”;//这里双引号改成英文双引号 this.wahaha = wahaha ;

//输出undefined console.log(wahaha +"\n"+ this.wahaha +"\n"+ ClassA.wahaha ); } ClassA.wahaha = “wahaha” ;

var a = new ClassA(); console.log("\n" + wahaha +"\n"+ this.wahaha +"\n"+ ClassA.wahaha );`

楼主的意思是在window对象里用var声明变量,该变量可以顺利成为window的公开属性,而在自定义的class里确不能,

同疑惑,哈哈,不过javascript作为弱语言,很多特性都跟它的解释器有很大的关系,偶尔出点幺儿子也是很正常的

o(∩_∩)o 哈哈 终于有人明白我的帖子具体是在说什么的了~~ 对的,就是您说的这个疑惑~~我只能暂且把这个情况作为一个特例,记着了……

foo.bar() // 上下文是foo(this === foo)
foo['bar']() // 上下文是root

上面两行代码我经过测试,您说的这个情况不成立啊!不知道您的测试代码是怎样的?着两行的测试结果应该是一样的才对,无论什么情况下。

摘自《深入浅出CoffeeScript》:

回忆一下,下面对CoffeeScript中上下文的规则做了个总结,前面的的规则优先于后面的规则: (1). 当在一个函数调用之前有new关键字,则上下文为新建的对象; (2). 当一个函数使用call或者apply调用时,给定的第一个参数即为上下文; (3). 否则,如果一个函数作为一个对象的属性(obj.func)或者obj[‘func’])来调用时,它就把该对象作为上下文来运行; (4). 如果与上述几条都不符的话,则函数将在全局上下文中运行。

继续来分析你的问题: 这是一个作用域和上下文的问题。在JavaScript中,this指向当前的上下文,而var定义的变量值在当前作用域中有效。 JavaScript有两种作用域,全局作用域和局部作用域。局部作用域就是在一个函数里。 var关键字使用来在当前作用于中创建局部变量的,而在浏览器中的JavaScript全局作用域中使用var语句时,会把申明的变量挂在window上,而全局作用域中的this上下文恰好指向的又是window,因此在全局作用域中var申明的变量和window上挂的变量,即this可访问的变量有间接的联系,但没有直接联系,更不是一样的。

考虑new一个函数,根据上面的(1)知道,执行构造函数时,上下文this为新建的对象(该对象的原型链__proto__指向了该函数的prototype,这就是原型继承,顺便提一下,与本问题无关),而作用域确实局部的,与this完全不同,所以自然没有什么联系,双方的赋值不会互相影响。

形象点说: 作用域就是一个执行过程,这个执行过程在JavaScript中就是一个函数,这个执行过程是如何和外界互相沟通的呢? 1.参数和返回值 2.上一级的作用域 3.this,即上下文 在作用域中创建的变量,函数执行外就被清除了。

@xlqstar 是,不好意思,我错了,应该是这样:

foo.bar() // 上下文是foo(this === foo)
var bar = foo.bar
bar() // 上下文是root
bar.apply(obj) // 上下文是obj
bar.call(obj) // 上下文是obj

@xlqstar 可是你的例子本身就不是同一个,你前端用了A例子,后端用了B例子,A!==B,没有可比性,所以出不来你想要的结果么。

@island205 非常感谢您的剖析,您提到的"作用域就是一个执行过程"这句话很有道理,但有些地方具体一推敲又会又很多疑惑,继续刨根问底下去代价太大,这应该跟js的运行机制有关系,我还是宁愿把此作为特殊情况记住得了。 另外我at一下 @BYVoid 看看他有什么好的解释不

at错了 重新at下 @byvoid

@xlqstar 这些东西,多接触接触就会立即的。

用this定义的是成员变量,用var定义的是局部变量,

function ClassA(){ var wahaha = ‘娃哈哈’; console.log(wahaha); console.log(this.wahaha); console.log(this); }

var a = new ClassA(); ClassA();

试试上面的语句,有助于理解。 1楼回复的对。。

回到顶部