- 作用域链
- 由于作用域是相对于变量而言的,而如果存在多级作用域,这个变量又来自于哪里?这个问题就需要好好地探究一下了,我们把这个变量的查找过程称之为变量的作用域链
- 作用域链的意义:查找变量(确定变量来自于哪里,变量是否可以访问)
- 简单来说,作用域链可以用以下几句话来概括:(或者说:确定一个变量来自于哪个作用域)
- 查看当前作用域,如果当前作用域声明了这个变量,就确定结果
- 查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明
- 再查找上级函数的上级函数,直到全局作用域为止
- 如果全局作用域中也没有,我们就认为这个变量未声明(xxx is not defined)
var name = 'sunny';
(()=>{
var name = 'storm';
console.log(name);
})();
var name = 'sunny';
(()=>{
console.log(name);
var name = 'storm';
})();
var name = "sunny";
var f1 = ()=> {
return ()=>{
console.log(name);
}
var name="storm";
}
var nameNew = f1();
nameNew();
var name = "sunny";
var f1 = ()=>{
return {
say:()=>{
console.log(name);
var name = "storm";
}
}
}
var nameNew = f1();
console.log(nameNew.say());
- 闭包函数的实现
- 函数执行完毕后,作用域中保留了最新的timer变量的值
- 闭包函数常应用于模块化开发,防止变量被破坏
var count = ()=>{
var timer = 0;
var c = ()=>{
return timer++;
}
return c;
}
var counter = count();
console.log(counter());
console.log(counter());
console.log(counter());
-
闭包的创建
- 先用外层函数封装一个受保护的局部变量
- 再在内层函数中操作外层函数的变量
- 外层函数将内层函数返回到外部,在外部反复调用
-
闭包的判断
- 函数嵌套!
- 内层函数使用外层函数的局部变量
- 内层函数被返回到外部,在外部调用
-
判断闭包输出
- 同一次外层函数调用返回的内层函数,操作同一个变量
- 外层函数调用了几次,就有几个受保护的变量副本
-
闭包的优缺点
- 优点:即要重用变量,又要保护变量不被污染
- 缺点:占用更多内存空间——因为outer的活动对象无法释放
-
函数的四种调用方式
- 在ES6的箭头函数之前的时代,想要判断一个函数内部的this指向谁,就是根据这四种方式来决定的,函数内部的this跟大小写、书写位置无关
- 函数调用
- 方法调用
- 构造函数调用(new)
- 上下文方式调用(call、apply、bind)
// 函数调用,this指向window
var age = 18;
var person = {
age:15,
say:function(){
console.log('我今年' + this.age + '岁!');
}
}
var sunny = person.say;
sunny();
// 方法调用,this指向对象person
var age = 18;
var person={
age:15,
say:function (){
console.log('我今年' + this.age + '岁!');
}
}
person.say();
// 构造函数调用,this将指向构造函数实例本身
var age=18;
var person={
age:15,
say:function(){
console.log('我今年' + this.age + '岁!');
}
}
new person.say();
//上下文调用方式,有3种,call、apply、bind
function f1(){
console.log(this);
}
//call方法的第一个参数决定了函数内部的this的值
f1.call([1,3,5])
f1.call({age:20,height:1000})
f1.call(1)
f1.call("abc")
f1.call(true);
f1.call(null)
f1.call(undefined);
//上述代码可以用apply完全替换
//总结:
//call方法的第一个参数:
//1、如果是一个对象类型,那么函数内部的this指向该对象
//2、如果是undefined、null,那么函数内部的this指向window
//3、如果是数字-->this:对应的Number构造函数的实例
// --> 1 --> new Number(1)
// 如果是字符串-->this:String构造函数的实例
// --> "abc" --> new String("abc")
// 如果是布尔值-->this:Boolean构造函数的实例
// --> false --> new Boolean(false)
- bind()函数
// 普通方法调用
var person = {
age:18,
run : function(){
console.log(this); //this指向person
var _that=this;
setTimeout(function(){
console.log(this.age); //this指向window
console.log(_that.age);
},50);
}
}
person.run();
// 通过执行了bind方法,匿名函数本身并没有执行,只是改变了该函数内部的this的值,指向person
var person = {
age:18,
run : function(){
console.log(this); // this指向person
setTimeout((function(){
console.log(this.age);
}).bind(this),50); // 绑定this指向person
}
}
person.run();
// bind函数基本用法
function speed(){
console.log(this.seconds);
}
speed({ seconds:100 });
// 执行了bind方法之后,产生了一个新函数,
// 这个新函数里面的逻辑和原来还是一样的,唯一的不同是this指向{ seconds:100 }
var speedBind = speed.bind({ seconds:100 });
speedBind(); //100
// bind函数常规写法
(function eat(){
console.log(this.seconds);
}).bind({ seconds:360 })()
// bind函数案例
var obj={
name:"西瓜",
drink:(function(){
//this指向了:{ name:"橙汁" }
console.log(this.name);
}).bind({ name:"橙汁" })
}
obj.drink(); //"橙汁"
var p10={
height:88,
run:function(){
//this
setInterval((function(){
console.log(this.height); //88
}).bind(this),100)
}
}
p10.run();
// 手写bind函数
Function.prototype._bind = target => {
// 这里的this指向函数实例
// target表示新函数的内部的this的值
// 利用闭包创建一个内部函数,返回那个所谓的新函数
return () => {
this.call(target);
}
}
function person(){
console.log(this);
}
person();
var sunny = person.bind({age:18});
sunny();