我们先来看看常见形式
如果function foo(){}是作为赋值表达式的一部分的话,那它就是一个函数表达式
如果function foo(){}被包含在一个函数体内,或者位于程序的最顶部的话,那它就是一个函数声明。
比如
// 声明,因为它是程序的一部分
function foo(){}
//表达式,因为它是赋值表达式的一部分
var bar = function foo(){};
// 表达式,因为它是new表达式
new function bar(){};
(function(){
// 声明,因为它是函数体的一部分
function bar(){}
})();
还有一种函数表达式不太常见,就是被括号括住的(function foo(){}),他是表达式的原因是因为括号 ()是一个分组操作符,它的内部只能包含表达式
下面是我们常见的两种写法,因为JavaScript里括弧()里面不能包含语句,所以,解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明。
(function(){
var a;
//code
}());
(function(){
var a;
//code
})();
下面这种写法则不会被解析成立即执行函数
function(){
var a;
//code
}(1);
它等价于,一个函数表达式和一个常量表达式
function(){
var a;
//code
}
(1)
但是,括号有个缺点,那就是如果上一行代码不写分号,括号会被解释为上一行代码最末的函数调用,产生完全不符合预期,并且难以调试的行为,加号等运算符也有类似的问题。
所以一些推荐不加分号的代码风格规范,会要求在括号前面加上分号。
void function () {
var a = 100;
console.log(a);
}();
语义上 void 运算表示忽略后面表达式的值,变成 undefined
function showThis(){
console.log(this);
}
var o = {
showThis: showThis
}
showThis(); // global
o.showThis(); // o
调用函数时使用的引用,决定了函数执行时刻的 this 值。所以这里全局调用函数this指向global,使用对象调用函数,this指向对象
"use strict"
function showThis(){
console.log(this);
}
var o = {
showThis: showThis
}
showThis(); // undefined
o.showThis(); // o
当严格模式时使用,this 严格按照调用时传入的值,可能为 null 或者 undefined。
const showThis = () => {
console.log(this);
}
var o = {
showThis: showThis
}
showThis(); // global
o.showThis(); // global
箭头函数的this只与定义时的作用域有关。
class C {
showThis() {
console.log(this);
}
}
var o = new C();
var showThis = o.showThis;
showThis(); // undefined
o.showThis(); // o
因为 class 设计成了默认按 strict 模式执行。
函数在执行的时候,实际上默认调用了call方法
test() // 等价于test.call()
这是默认调用call方法。。但是当我给他加上参数。。它就完成一些很强大的功能。 函数调用call方法默认可以改变函数内部的this指向。 它的第一个参数,是this改变后指向的对象,后面的参数对应函数执行的参数。 举个例子:
//现在的this默认指向调用者
function Person(name){
this.name=name;
}
//我们创建一个空对象
var person={};
//那么我们想让Person中的this指向person怎么办
//这样
Person.call(person,'jackson');
//最后打印出person
person={
name:'jackson'
}
我们可以看到,调用了call后,实际上在Person执行的时候,this.name就变成了person.name。
apply的用法和call很像,它的第一个参数依旧是改变函数执行的时候的this指向,不同的是,函数执行的时候的各个形参,需要被放在一个数组里面,做为执行时候的第二个参数。
function Person(name,age){
this.name=name;
this.age=age;
}
var person={}
//第二个参数为数组
Person.apply(person,['Osborn',22])
bind的用法和前面两个都有所不同,它有延迟执行的特点,它返回一个新的函数。 bind()的第一个参数代表函数执行的this的指向,后面的参数可以用来执行函数执行时候的形参。
this.num = 9;
var mymodule = {
num: 81,
getNum: function() {
console.log(this.num);
}
};
mymodule.getNum(); // 81
var getNum = mymodule.getNum;
getNum(); // 9, 因为在这个例子中,"this"指向全局对象
var boundGetNum = getNum.bind(mymodule);
boundGetNum(); // 81
它还有一个很好的作用,就是预先指定参数, 只要将这些参数(如果有的话)作为bind()的参数写在this后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
function list() {
//这里相当于将函数参数放到一个数组中返回
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// 预定义参数37
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
let a = {};
let fn = function () {
console.log(this);
};
fn.bind().bind(a)(); // => ?
不管我们给函数 bind 几次,fn 中的 this 永远由第一次 bind 决定,所以结果永远是 window。
function _new(func, ...args) {
let obj = Object.create(func.prototype); // 原型
let res = func.apply(obj, args); // 初始化对象属性
return res instanceof Object ? res : obj; // 返回值
}