console.log(a); // undefined
var a = 1;
复制代码
function varscope(){
foo = "I'm in function"; //直接赋值 没有声明
console.log(foo);//I'm in function
}
varscope();
console.log(window.foo); //I'm in function
复制代码
function testOrder(arg) {
console.log(arg); // arg是形参,不会被从新定义
console.log(a); // 由于函数声明比变量声明优先级高,因此这里a是函数
var arg = 'hello'; // var arg;变量声明被忽略, arg = 'hello'被执行
var a = 10; // var a;被忽视; a = 10被执行,a变成number
function a() {
console.log('fun');
} // 被提高到做用域顶部
console.log(a); // 输出10
console.log(arg); // 输出hello
};
testOrder('hi');
/* 输出: hi function a() { console.log('fun'); } 10 hello */
复制代码
函数做用域javascript
函数做用域内,对外是封闭的,从外层的做用域没法直接访问函数内部的做用域html
function bar() {
var testValue = 'inner';
}
console.log(testValue); // 报错:ReferenceError: testValue is not defined
复制代码
经过 return 访问函数内部变量:java
function bar(value) {
var testValue = 'inner';
return testValue + value;
}
console.log(bar('fun'));// "innerfun"
复制代码
经过 闭包 访问函数内部变量:闭包
function bar(value) {
var testValue = 'inner';
var rusult = testValue + value;
function innser() {
return rusult;
};
return innser();
}
console.log(bar('fun')); // "innerfun"
复制代码
当即执行函数做用域app
这是个很实用的函数,不少库都用它分离全局做用域,造成一个单独的函数做用域;它可以自动执行(function() { //... })()
里面包裹的内容,可以很好地消除全局变量的影响;函数
<script type="text/javascript"> (function() { var testValue = 123; var testFunc = function () { console.log('just test'); }; })(); console.log(window.testValue); // undefined console.log(window.testFunc); // undefined </script>
复制代码
块级做用域ui
在 ES6 以前,是没有块级做用域的概念的。this
for(var i = 0; i < 5; i++) {
// ...
}
console.log(i) // 5
复制代码
很明显,用 var 关键字声明的变量,在 for 循环以后仍然被保存这个做用域里;es5
这能够说明: for() { }仍然在,全局做用域里,并无产生像函数做用域同样的封闭效果;spa
若是想要实现 块级做用域 那么咱们须要用 let 关键字声明
for(let i = 0; i < 5; i++) {
// ...
}
console.log(i) // 报错:ReferenceError: i is not defined
复制代码
在 for 循环执行完毕以后 i 变量就被释放了,它已经消失了!!!
一样能造成块级做用域的还有 const 关键字:
if (true) {
const a = 'inner';
}
console.log(a); // 报错:ReferenceError: a is not defined
复制代码
let 和 const 关键字,建立块级做用域的条件是必须有一个 { } 包裹:
词法做用域
当咱们要使用声明的变量时:JS引擎总会从最近的一个域,向外层域查找
testValue = 'outer';
function afun() {
var testValue = 'middle';
console.log(testValue);// "middle"
function innerFun() {
var testValue = 'inner';
console.log(testValue);// "inner"
}
return innerFun();
}
afun();
console.log(testValue); // "outer"
复制代码
当 JS 引擎查找变量时,发现全局的 testValue 离得更近一些,则取全局的testValue的值即 outer
var testValue = 'outer';
function foo() {
console.log(testValue);// "outer"
}
function bar() {
var testValue = 'inner';
foo();
}
bar();
复制代码
动态做用域
动态做用域,做用域是基于调用栈的,而不是代码中的做用域嵌套;
做用域嵌套,有词法做用域同样的特性,查找变量时,老是寻找最近的做用域;
this
关键字在一个函数中,this老是指向当前函数的全部者对象,this老是在运行时才能肯定其具体的指向, 也才能知道它的调用对象。
window.name = "window";
function f(){
console.log(this.name);
}
f();//window
var obj = {name:'obj'};
f.call(obj); //obj
复制代码
在执行f()时,此时f()的调用者是window对象,所以输出”window”
f.call(obj) 是把f()放在obj对象上执行,至关于obj.f(),此时f中的this就是obj,因此输出的是”obj”
对比如下两段代码:
var foo = "window";
var obj = {
foo : "obj",
getFoo : function(){
return function(){
return this.foo;
};
}
};
var f = obj.getFoo();
f(); //输出'window'
/* 分析 执行var f = obj.getFoo()返回的是一个匿名函数,至关于: var f = function(){ return this.foo; } f() 至关于window.f(), 所以f中的this指向的是window对象,this.foo至关于window.foo, 因此f()返回"window" */
复制代码
var foo = "window";
var obj = {
foo : "obj",
getFoo : function(){
var that = this;
return function(){
return that.foo;
};
}
};
var f = obj.getFoo();
f(); //输出'obj'
/* 分析 执行var f = obj.getFoo() 一样返回匿名函数,即: var f = function(){ return that.foo; } 惟一不一样的是f中的this变成了that, 要知道that是哪一个对象以前,先肯定f的做用域链:f->getFoo->window 并在该链条上查找that,此时能够发现that指代的是getFoo中的this, getFoo中的this指向其运行时的调用者,从var f = obj.getFoo() 可知此时this指向的是obj对象,所以that.foo 就至关于obj.foo,因此f()返回"obj" */
复制代码
箭头函数有两种格式:
var fn = x => x * x; //只包含一个表达式,连{ ... }和return都省略掉了
x => { //还有一种能够包含多条语句,这时候就不能省略{ ... }和return:
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}
复制代码
箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法做用域,由上下文肯定。
对比如下两个例子
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth;
};
return fn();
}
};
复制代码
//箭头函数彻底修复了this的指向,this老是指向词法做用域,也就是外层调用者obj:
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); // 29
//因为this在箭头函数中已经按照词法做用域绑定了,因此,用call()或者apply()调用箭头函数时,没法对this进行绑定,即传入的第一个参数被忽略:
var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; // 1990
var fn = (y) => y - this.birth; // this.birth还是1990
return fn.call({birth:2000}, year);
}
};
obj.getAge(2015); // 25
复制代码
箭头函数与this结合例子
var name = 'window'
var person1 = {
name: 'person1',
show1: function () {
console.log(this.name)
},
show2: () => console.log(this.name),
show3: function () {
return function () {
console.log(this.name)
}
},
show4: function () {
return () => console.log(this.name)
}
}
var person2 = { name: 'person2' }
person1.show1() //person1
person1.show1.call(person2) //person2
person1.show2() //window
person1.show2.call(person2) //window
person1.show3()() //window
/*person1.show3是一个高阶函数,它返回了一个函数,分步走的话,应该是这样: var func = person3.show() func() 从而致使最终调用函数的执行环境是window,但并非window对象调用了它。因此说,this老是指向调用该函数的对象,这句话还得补充一句:在全局函数中,this等于window。 */
person1.show3().call(person2)//person2 经过person2调用了最终的打印方法
person1.show3.call(person2)()//window 先经过person2调用了person1的高阶函数,而后再在全局环境中执行了该打印方法。
person1.show4()() //person1 箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
person1.show4().call(person2) //person1 箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象,用person2去调用这个箭头函数,它指向的仍是person1。
person1.show4.call(person2)() //person2 箭头函数的this指向的是谁调用箭头函数的外层function,箭头函数的this就是指向该对象,若是箭头函数没有外层函数,则指向window
复制代码
例题:
var number = 5;
var obj = {
number: 3,
fn1: (function () {
var number;
this.number *= 2;
number = number * 2;
number = 3;
return function () {
var num = this.number;
this.number *= 2;
console.log(num);
number *= 3;
console.log(number);
}
})()
}
var fn1 = obj.fn1;
fn1.call(null);
obj.fn1();
console.log(window.number);
复制代码
输出10 9 3 27 20