首页 > 作用域一些问题

作用域一些问题

``

var o = {
    fn:function(){ //为什么打印fn不会找到对象里面的fn,会到全局里面找fn
        console.log(fn);
    }
}
function  fn(){
    console.log('aa');
}
o.fn();   //发怒函数定义在对象里面,这里执行产出的作用域会指向全局作用域吗?

//大神们能画图解析一波吗?

``


大括号对{}在js里面有两个意思,一个是代码块,还有一个是对象直接量
你的var o = {...}很明显是对象直接量,里面是用于定义对象的,不会作为一个独立的作用域,所以当你在fn函数里面再调用fn的时候,解释器沿着作用域向上回溯不会到o对象内部,而是直接到了全局。


这个并不能说当前的作用域是全局作用域,如果console.log(this.fn);的结果是全局作用域下的fn才能说明当前作用域是全局作用域。


你可能想表达的,在这里隐含涉及了三个问题:1.变量的作用域;2.变量声明提升;3.对象方法中的this指向。

先说作用域的问题。你问“为什么打印fn不会找到对象里面的fn,会到全局里面找fn”,“全局”两字基本透露了作用域问题。JavaScript里面,函数是一等公民,在ES6之前,function是确定作用域的唯一条件,使用var声明的变量,仅在当前函数或全局有效,但现在可以使用const声明的变量拥有常量的性质,使用let声明的变量作用域缩小为块,也就是诸如if, for的{}内部。如果不考虑新的用法,原来的JavaScript声明变量只有两种形式,即var, function。如果在function内没有找到使用的变量声明,那么会往父一级的作用域去找,直到找到为止,否则就是undefined。你问题中,在function内使用fn这个变量,function内部没有声明,自然会往它的上一级作用域(也就是全局)去找。

再说下声明提升。一般,我们会在使用变量前声明并赋值变量。由于JavaScript的变量给出的时候不像C或Java还要规定数据类型,所以JavaScript的变量类型是在赋值的时候随时获得的。如果在使用一个变量之前没有var声明,在严格模式"use strict"下会报错,否则会是undefined。但是有一些情况不同,就是使用变量之后声明,举两个例子吧:

function a() {
  alert(b)
}
a();
var b = 10;

这是一个例子,在执行a()之前,b并没有被声明,也没有被赋值。第一次执行a()的时候,却能正确执行函数,虽然弹出的对话框显示undefined。这就是一个声明提升的例子。在JavaScript里面,var声明如果出现在靠后的位置,会按照一定的规则提升到前面,所以上面的代码会被解释成:

var b
function a() {
  alert(b)
}
a();
b = 10;

程序并不会报错,因为b进行了var声明。所以,同一级别的作用域范围内,只要使用var声明了变量,无论它的位置在什么地方,都会被提前到开始去(赋值还是按照原来的顺序赋值)。当然,function声明的同时,也会被提升,这也是为什么我们可以把函数声明放在文档最后的原因,函数声明的同时,变量的类型(也就是function)也确定了,而且很有意思的是,由于function的一等公民身份和object本质,它是引用型数据类型,所以实际上即便在文档末尾声明function,它的提升也会更加霸道,相当于声明和赋值都会提升到文档开头。给个例子:

function a() {
  console.log(b);
  console.log(c);
}
a(); 
function b(){
  return 'b ok';
};
var c = function() {
  return 'c ok';
};
a();

上面的代码中虽然b和c最后都是函数,但是实际上有本质的不同,虽然它们的声明都会提升,但是c仅仅提升的是var c;而b则会整个函数声明都会被提升,把function b(){}放在文档的任何位置都没有区别。

总之,由于声明提升的原因,你题目中fn函数虽然放在下面,仍然会在上面的使用时有效。

第三,来说下对象中的this指向。虽然你的题目里面没有提到这个问题,但是你问题中“找到对象里面的fn”这句话就隐含了这个问题。JavaScript的对象是引用类型数据,所以对象的属性或方法不能用变量来类比,跟变量不是一回事,所以当你在function中使用fn时使用的是一个变量,跟对象的fn方法没有半毛钱关系,自然不会找到对象里面的fn方法。而楼上提到,如果你把题目中的代码改为console.log(this.fn)则问题就比较复杂一些,就会涉及到this的指向问题。但退一步说,排除你把对象方法名和变量等价的因素以外,你想让fn找到对象里面的fn,那么就必须用this.fn。this我总结出来,90%的情况指向的是当前的实例化对象。你只需要找你当前使用的this所在的作用域是否是某个对象实例的方法即可。举个例子:

var age = 0;
function Cat() {
  this.name = 'hello kitty';
  this.age = function() { this.age ++; return age;} ();
  this.sing = function() { alert(this.age); }
}
var cat = new Cat();

请问这个时候cat的age和sing方法各自回得到什么样的结果,代码中的this.age各自代表什么?其实唯一复杂一点的是this.age ++这里。因为这个this所在的作用域是一个函数,而并非任何对象的某个方法,所以this指向的是全局,this.age也就等于最前面的0.

你的问题虽然简单,但是涉及的东西也挺多,我也是学习中,有机会多交流。


js没有类,也没有类内部空间这样的概念(所以没有C++之类的public/protected/private的限定关键词)。
一个在对象内部的函数,和一个在全局的函数,行为上没啥差别。

function x(){}
var a={
    x:function{}
}
var b={
    x:function{}
    y:function{
        x()//未指定作用域,则去调用全局作用域下的x
        a.x()//指定了作用域a,则去调用a中的x
        this.x()//指定了作用域this,则去调用当前作用域下的x(即b.x)
    }
}

你这里 console.log(fn) 里的 fn 其实和对象 o 里面的 fn 属性一点儿关系都没有……

【热门文章】
【热门文章】