call与apply bind
同:都是都是function对象原型上的方法,用来改变this指向的
不同:传参形式不同call(obj,10,20,30) allpy(obj,[10,20,30])函数直接执行
还有一个方法bind,bind传参与call相同,但是返回的是一个函数,它不会马上执行。
call的性能更好,尤其是传给函数参数大于三个时,这种方式会返回一个新函数,但不会马上执行。
apply:
func.apply(thisArg, [argsArray])
thisArg: 必选,func函数运行时的this值。指定为null或undefined时会自动替换为指向全局对象。
js中new操作符都干了什么
new操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象。
1、创建一个空的对象
2、将构造函数作用域赋给新对象(因此this就指向了这个新对象)
3、执行构造函数(为这个新对象添加属性)
4、返回新对象 (所以函数中不需要return)
this的问题
函数内部
this的值取决于函数被调用的方式。
在非严格模式下,且this的值不是由调用设置的,所以this的值默认指向全局对象,即window。;在严格模式下,如果进入执行环境中没有设置this的值,this的值为undefined。
function fn1(){
return this
}
fn1()===window
function fn2(){
"use strict"
return this
}
fn2() === undefined //true
类上下文
this的表现与在函数中类似(类本质上也是函数)
在类的构造函数中,this是一个常规对象,类中所有的非静态方法都会被添加到this的原形中。(静态方法不是this的属性,它们只是类自身的属性)
类中的this
和普通函数一样,方法中的this取决于它们如何被调用,有时,改写行为,让类中的this值总是指向这个类实例会很有用。
constructor() {
// Bind sayBye but not sayHi to show the difference
this.sayBye = this.sayBye.bind(this);
}
类内部总是严格模式。
this和对象转换
function add(c,d){
return this.a+this.b+c+d
}
let o = {a:1,b:3}
//第一个参数用作“this”的对象。其余用作函数的参数
add.call(o,5,7) // 16
add.apply(o,[10,20]) // 34
非严格模式下使用call和apply时,如果用作this的值不是对象,则会被尝试转换为对象。null和undefined被转换为全局对象。其他类型会使用相应的构造函数转换为对象。
function bar(){
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
bar.call('foo'); // [object String]
bar.call(undefined); // [object global]
bind方法
调用bind方法会创建一个新函数,this将永远的被绑定到bind的第一个参数,无论后续这个函数如何被调用。
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, azerty, azerty
作为对象的方法
this的绑定只受最接近的成员引用的影响。
原型链中的this
记住原型链的特性,先在自身找,由于继承了原型链上的属性(方法),所以函数的this指向当前对象。也就是函数是作为当前对象的方法调用的,所以它的this指向了当前对象。
作为构造函数
当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。
构造函数默认是返回this所指向的那个对象,但它也可以手动返回一个对象(如果返回值不是一个对象,则返回this对象)
function C(){
this.a = 37;
}
var o = new C();
console.log(o.a); // logs 37
function C2(){
this.a = 37; //“僵尸代码”,对外部没有任何影响
return {a:38}; //手动返回对象
}
o = new C2();
console.log(o.a); // logs 38
作为一个DOM事件处理函数
当函数被用作事件处理函数时,它的this指向触发事件的元素。
作为一个内联事件处理函数
当代码被内联on-event处理函数
调用时,它的this指向监听器所在的DOM元素。
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
当没有设置内部函数的this时,它会指向global/window对象(非严格模式下)
<button onclick="alert((function(){return this})());">
Show inner this
</button>
函数表达式
函数表达式和函数声明的最主要区别是函数名称,在函数表达式中可省略它,从而创建匿名函数。一个函数表达式可被用作IIFE(立即执行函数)。
函数表达式提升
函数表达式没有提升,与函数声明不同。
命名函数表达式
如果要在函数体内部引用当前函数,则需要创建一个命名函数。函数名称将只会作为函数体(作用域)的本地变量(即不可在外部进行调用)。
展开语法
函数调用时
等价于apply的方式
function myfunc(x,y,z){ }
let args = [1,2,3];
myfunc.apply(null,args)
//使用展开语法
myfunc(...args);
在new表达式中使用
使用new关键字来调用构造函数时,不能直接使用数组+apply的方式(apply调用的是[[ Call ]],而不是构造 [[Constructor]])。
var dateFields = [1970, 0, 1]; // 1970年1月1日
var d = new Date(...dateFields);
使用的便捷性
- 构造字面量数组时更便捷
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
- 数组拷贝
let arr = [1,2,3]
let arr2 = [...arr] //[1,2,3]
实际展开语法和Object.assign()
行为一致,执行的都是浅拷贝。
构建对象字面量时
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象: { foo: "baz", x: 42, y: 13 }
不能替换或模拟Object.assign()
函数。(具体参看mdn官方文档)
只能用于可迭代对象
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
var array2 = {...obj} // {'key1': 'value1'}
in操作符
如果指定的属性在指定的对象或其原型链中,则in
运算符返回true。
// 数组
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees // 返回true
3 in trees // 返回true
6 in trees // 返回false
"bay" in trees // 返回false (必须使用索引号,而不是数组元素的值)
"length" in trees // 返回true (length是一个数组属性)
对于继承来的属性,in运算符也会返回true。
arguments
是一个传递给函数的参数的类数组对象。
arguments对象是所有(非箭头)函数中都可用的局部变量,可以用下面的方式引用:
arguments[0];
arguments[1];
arguments对象不是一个Array,它类似Array,但只有length属性和索引元素,也不能进行pop.但它可以被转换为一个真正的Array:
let args = Array.prototype.slice.call(arguments);
let args = [].slice.call(arguments);
对参数使用扩展语法:使用Array.from()
或 扩展运算符讲将参数转换为真实数组。
let args = Array.from(arguments);
let args = [...arguments]
属性:
arguments.callee:指向参数所属的当前执行的函数;指向调用当前函数的函数。
arguments.length:传递给函数的参数数量。
argumnens[@@iterator]:返回一个新的Array迭代器对象,该对象包含参数中每个索引的值。