函数绑定方式(即this指向)
函数绑定和this指向在js中非常的重要,不同的函数绑定方式会影响函数中this的指向。函数主要的绑定方式有以下四种:
- new绑定
- 默认绑定
- 隐式绑定
- 显式绑定
思考:函数声明有几种方式?
下面通过具体的面试题目来做一个代码逻辑分析:
new绑定
知识点: 箭头函数
setTimeout
就近原则查找
捕获上下文变量
this和变量的指向是不一样的
构造函数
var a = 1;
a = 2;
window.a = 3;
function Test() {
let a = 4;
this.a = 5;
setTimeout(function () {
// 输出 4,就近原则
console.log(a);
}, 10);
setTimeout(function () {
// 输出 3,this被指向window
console.log(this.a);
}, 20);
setTimeout(() => {
// 输出 4,箭头函数 捕获上下文变量
console.log(a);
}, 30);
setTimeout(() => {
// 输出 5,箭头函数无this,捕获上下文this
console.log(this.a);
}, 40);
// 此时window.a 是多少呢?结果是3;在浏览器和浏览器控制台中执行 a, window.a 是一个对象
}
// new 绑定, this指向新对象
new Test();
复制代码
构造函数定义:通过new函数名来实例化对象的函数叫构造函数。任何的函数都可以作为构造函数存在。
默认绑定
知识点: 默认绑定this指向
直接调用
var a = 1;
a = 2;
window.a = 3;
function Test() {
let a = 4;
// 修改window
this.a = 5;
setTimeout(function () {
// 输出 5,this被指向window
console.log(this.a);
}, 20);
setTimeout(() => {
// 输出 5,箭头函数无this,捕获上下文this,上下文this指向window
console.log(this.a);
}, 40);
}
// 默认绑定 this指向window
Test();
复制代码
默认绑定练习
知识点:判断好绑定方式是确定this指向的关键一步
var number = 5;
var obj = {
number: 3,
// fn1定义时就是一个闭包,
fn1: (function () {
var number;
// 过程一:默认绑定,this指向window; this.number = 10
this.number *= 2;
number = number * 2;
number = 3;
// 自执行函数被执行过,fn1 = 下面的函数体,在过程二中只执行函数内的代码;且过程一已经改变了obj的值了
return function () {
// 过程一:默认绑定,this指向window
// 过程二:隐式绑定,注意过程一对全局参数的改变;this指向obj
var num = this.number;
// 过程一:window.number 已经是20了
this.number *= 2;
// 过程一: num 输出 10
// 过程二: 这里的this是obj 输出 3
console.log(num);
number *= 3;
// 过程一:输出 9;闭包指向 var number;
// 过程二:输出 27;闭包
console.log(number);
}
})()
}
// 过程一
// 隐式绑定, 但fn1在声明式就形成了一个闭包, 这段代码nmuber不会超出自执行函数的范围,于是obj下的number就没有用了
var fn1 = obj.fn1;
// 显式绑定null,undefined时 会变成默认绑定,最终还是隐式绑定,不过此时的fn1指向引用和obj没有关系,可以看做是【默认绑定】
// 判断好绑定方式是确定this指向的关键一步
fn1.call(null);
// 过程二
// 隐式绑定
obj.fn1();
console.log(window.number);
结果:
10
9
3
27
20
复制代码
这道题要把this的指向变化和闭包的逻辑分开来看, 这里特别容易混淆。
关联知识点
闭包(closure)
在函数外部自然无法读取函数内的局部变量
复习一下什么是闭包?
简单讲即两种情况:
- 函数作为返回值
- 函数作为参数传递
域 Scopes
- ES6有了 block块级作用域
- closure 闭包的内容
一道关于闭包的题目
var x = 1, y = 2;
var z = function () {
var x = 2;
return {
// 就近原则(return时, x被赋值等于2),注意此时的x: 2,和上面的var x = 2; 已经没有关系了
x: x,
// y相当于函数名,此处就是一个闭包
y: function (a, b) {
// 闭包的参数会从父函数中查找,即var x = 2,当前代码下,当y被执行时x变成了3
x = a + b;
},
z: function () {
// 当a.z()时 返回的x为3
return x;
}
}
};
// 默认绑定
a = z();
// 闭包中的x变成了3
a.y(x, y);
console.log(a.z(), a.x, x);
结果:
3
2
1
复制代码
a = z();时 观察一下a对象
再添加一个变量w来观察一下,更加直观
var x = 1, y = 2;
var z = function () {
var x = 2;
// 新增一个变量
var w = 3;
return {
x: x,
y: function (a, b) {
x = a + b;
console.log(y);
console.log(w);
},
z: function () {
return x;
}
}
};
a = z();
复制代码
思考: 闭包中的this指向谁?
拓展
箭头函数编译后的es5怎么实现的?
// 源代码
function Test() {
setTimeout(() => {
console.log(this.a);
}, 40);
}
// 编译完成
"use strict";
function Test() {
// 是不是很熟悉,babel用箭头函数变了个魔术
var _this = this;
setTimeout(function () {
console.log(_this.a);
}, 40);
}
复制代码
箭头函数的优缺点?
在vue的methods、watch中,不应该使用箭头函数来定义 watcher 函数。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.vue选项中的方法将是 undefined。
函数和方法的区别?
函数(function)是一段代码,需要通过名字来进行调用。它能将一些数据(函数的参数)传递进去进行处理,然后返回一些数据(函数的返回值),也可以不返回数据。
function whoami() {
console.log(this);
}
复制代码
方法(method)是通过对象调用的javascript函数。也就是说,方法也是函数,只是比较特殊的函数。
let obj = Object();
obj.whoami = whoami;
obj.whoami();
复制代码
当将函数和对象和写在一起时,函数(function)就变成了方法(method)。
什么是解析器?
JavaScript解析器的作用是将JavaScript代码分解成AST, 具体的过程要认真了解一下编译原理。
闭包的优缺点?
IIFE: Immediately Invoked Function Expression(立即执行函数表达式)
- 在javascript(ES5)中,是没有块级作用域的概念的。
- 创建块级(私有)作用域,避免了向全局作用域中添加变量和函数,因此也避免了多人开发中全局变量和函数的命名冲突
- IIFE中定义的任何变量和函数,都会在执行结束时被销毁
函数声明有几种方式?
1.直接声明;2.函数表达式(匿名函数) www.cnblogs.com/sticktong/p…