1.这个也就是变量声明提前的作用了
alert(a); var a = 1;
结果是 undefined ,不会报错
2. a 还是 全局变量,在js中只有在obj、function内部才算自己的作用域,其中的变量才是局部变量
if (!("a" in window)) { var a = 1; } alert(a);
结果是: undefined
大叔解释:
(1)所有的全局变量都是window的属性,语句 var a = 1;等价于window.a = 1;
(2)所有的变量声明都在范围作用域的顶部
(3)变量声明被提前了,但变量赋值不会
主要原因:当变量声明和赋值在一起用的时候,JavaScript引擎会自动将它分为两部以便将变量声明提前,不将赋值的步骤提前是因为他有可能影响代码执行出不可预期的结果。
所以上面代码等价于:
var a; if (!("a" in window)) { a = 1; } alert(a);
意思就是:首先声明a,然后判断a是否在存在,如果不存在就赋值为1,很明显a永远在window里存在,这个赋值语句永远不会执行,所以结果是undefined。
注: 提前 这个词语显得有点迷惑了,其实就是执行上下文的关系,因为执行上下文分2个阶段:进入执行上下文和执行代码,在进入执行上下文的时候,创建变量对象VO里已经有了:函数的所有形参、所有的函数声明、所有的变量声明
VO(global) = {
a: undefined
}
这个时候a已经有了;
大叔注:相信很多人都是认为a在里面不可访问,结果才是undefined的吧,其实是已经有了,只不过初始值是undefined,而不是不可访问。
填充VO的内容顺序:函数的形参 -> 函数申明 -> 变量申明。
3.这个函数其实是一个有名函数表达式,函数表达式不像函数声明一样可以覆盖变量声明,但你可以注意到,变量b是包含了该函数表达式,而该函数表达式的名字是a;
不同的浏览器对a这个名词处理有点不一样,在IE里,会将a认为函数声明,所以它被变量初始化覆盖了,就是说如果调用a(--x)的话就会出错,而其它浏览器在允许在函数内部调用a(--x),因为这时候a在函数外面依然是数字。
基本上,IE里调用b(2)的时候会出错,但其它浏览器则返回undefined。
var a = 1, b = function a(x) { x && a(--x); }; alert(a);
结果就是 1
其实这个就相当于:
var a = 1,
b = function(x) {
x && b(--x);
};
alert(a);
变量b被赋值了一个匿名函数。
其实如果赋上名字的话,别人也无法通过名字来使用函数,只能是调用b,
eg :(这里面也要主要的东西。。。当然还是涉及到浏览器的问题,IE会把表达式的函数也当做函数,去提前声明函数,好吧刚才试了一下,新的IE浏览器基本也不存在这个问题了)
var c = function test ( ){ return 1;}
test(); => 报错:Uncaught ReferenceError: test is not defined
c(); => 1
(1): 变量声明在进入执行上下文的时候就完成
(2):函数声明也是提前的,所有的函数声明都在执行代码之前都已经完成了声明,和变量声明一样。
函数声明是:
function functionName ( arg1 , arg2 ) {
//函数体
};
如果是函数表达式,相当于变量赋值。函数表达式没有提前,就是相当于平时的变量赋值。
(3) 函数声明会覆盖变量声明,但不会覆盖变量赋值。
function value(){ return 1; } var value; alert(typeof value); //"function"
这里还说明: 函数声明的优先级高于变量声明的优先级
function value(){ return 1; } var value = 1; alert(typeof value); //"number"
该value赋值以后,变量赋值初始化就覆盖了函数声明。
4.
function a(x) { return x * 2; } var a; alert(a);
结果是: function a(x) {return x * 2;}
函数声明和变量声明的关系和影响,遇到同名的函数声明,VO不会重新定义,所以这时候全局的VO应该是如下这样的:
VO(global) = {
a: 引用了函数声明“a”
}
但是:
function a(x) { return x * 2; } var a = 3; alert(a);
结果就是: 3
应该还是这么解释吧: 变量与函数同名时,不会重新定义,但是给变量赋值以后,变量就会把函数声明给覆盖掉,其实可以这么理解,一个名字只可以对于一块内存区域,
所以当变量把具体的值放入内存的时候就会覆盖以前放函数声明的那块区域。
5.函数arguments的考察,arguments是对象,是对象,是对象。。。元素的集合
function b(x, y, a) { arguments[2] = 10; alert(a); } b(1, 2, 3);
结果是 : 10
函数上下文中的变量对象的问题,活动对象是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。arguments属性的值是Arguments对象:
AO = {
arguments: <ArgO>
};
arguments还是一个很复杂的对象,(值得好好总结)
function t(m,n,p){ console.log(arguments) } t( 1, 2, 3)
结果是:
[1, 2, 3]0: 11: 22: 3callee: t( m,n,p )arguments: nullcaller: nulllength: 3name: "t"prototype: tconstructor: t( m,n,p )__proto__: Object__proto__: ()apply: apply()arguments: (...)get arguments: ThrowTypeError()set arguments: ThrowTypeError()bind: bind()call: call()caller: (...)get caller: ThrowTypeError()set caller: ThrowTypeError()constructor: Function()length: 0name: ""toString: toString()__proto__: Object<function scope><function scope>length: 3Symbol(Symbol.iterator): ArrayValues()__proto__: Object
Arguments对象是活动对象的一个属性,它包括如下属性:
- callee — 指向当前函数的引用
- length — 真正传递的参数个数
- properties-indexes (字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)。 properties-indexes内部元素的个数等于arguments.length。properties-indexes 的值和实际传递进来的参数之间是共享的。
这个共享其实不是真正的共享一个内存地址,而是2个不同的内存地址,使用JavaScript引擎来保证2个值是随时一样的,当然这也有一个前提,那就是这个索引值要小于你传入的参数个数,也就是说如果你只传入2个参数,而还继续使用arguments[2]赋值的话,就会不一致,
function b(x, y, a) { arguments[2] = 10; alert(a); } b(1, 2);
//结果是: undefined
//这时候因为没传递第三个参数a,所以赋值10以后,alert(a)的结果依然是undefined,而不是10
而下面的例子:
function b(x, y, a) {
arguments[2] = 10;
alert(arguments[2]);
}
b(1, 2);
结果是 : 10
从这两个例子足以说明 arguments 和 properties-indexes (按参数列表从左到右排列)是两块内存区域
5. 你真的知道this吗?this一直指代的是上下文的对象
function a() { alert(this); } a.call(null);
结果是: [object window]
因为null的上下文是全局window哟
this一直指代的是上下文的对象
证明:
var object = { method: function() { alert(this === object); //true } } object.method();
单独定义函数:函数内部的this也是等价于window的
function method() { alert(this === window); //true } method()
call的用法:call方法将上下文指向第一个参数,如果第一个传入的参数是null或者undefined,则将this指向全局window
function method() { alert(this === window); } method(); //true method.call(document); //false
call方法作为一个function执行代表该方法可以让另外一个对象作为调用者来调用,call方法的第一个参数是对象调用者,随后的其它参数是要传给调用method的参数(如果声明了的话)
如果第一个参数传入的对象调用者是null或者undefined的话,call方法将把全局对象(也就是window)作为this的值。所以,不管你什么时候传入null,其this都是全局对象window