什么是this
this指针指向一个对象, 具体指向哪个对象是在函数执行时才确定的。
不同情况下this的指向
1.(默认绑定)函数直接调用,在非严格模式下,this指向window,严格模式下,this指向undefined。
//放在普通函数中
function fn(){
console.log(this);
//window
}
fn();
//放在严格模式的函数中
function fn1(){
"use strict"
console.log(this);
//undefined
}
fn1();
2.(隐式绑定)对象函数调用,this指向调用函数的对象
function out(){
console.log(this.b);
}
let foo = {
a:20,
b:10,
print:function(){
console.log(this.a);
},
print2:out
}
foo.print();//20
foo.print2();//10
但是这种绑定也会出现隐式丢失的问题
var foo = {
gla:20,
print:function(){
console.log(this.gla);
},
}
foo.print();//20 这是指向的对象是foo
var gPrint = foo.print; //gPrint是函数别名
var gla = 30;//全局变量(所属是window)
gPrint();//30 这时候指向的对象是window
3.(new绑定) 构造函数调用,this指向新创建的对象
//ES5
function foo(a) {
this.a = a;
console.log(this);
}
var bar = new foo(2); // foo{a:2}
//ES6
class Person{
constructor(name){
this.name = name;
console.log(this);
}
print(){
console.log(this.name);
}
}
let p=new Person("小红");//Person {name:"小红"}
let p1=new Person("小名");//Person {name:"小明"}
p.print();//小红
p1.print();//小明
let p2 = p.print;
p2();//Uncaught TypeError: Cannot read property 'name' of undefined
4.在箭头函数中没有单独的this, this在箭头创建的时候绑定(继承),与声明所在的上下文相同(找外层函数的this即可)。
let people = {
name:function(){
console.log(this); // 这里的this可能存在隐式丢失的情况,所以不一定指向people
let first = ()=>{
console.log(this);//这里的this一定与外层函数的this一致
}
first();
},
age:()=>{
console.log(this);// 这里外层无函数,或者可以直接认为最外层函数就是window
}
}
people.name();// {name: ƒ, age: ƒ}
people.age();// window
let fun = people.name;
fun(); // window
如何改变this的指向(call apply bind )
1.call和apply
call(要指向的对象,参数1,参数2,参数3)
apply(要指向的对象,[参数1,参数2,参数3]),效果和call一样,只是把参数换成数组形式
function people(age,name){
console.log(this);
console.log('cato:'+this.cato+' age:'+age+',name:'+name);
}
var cato = 'none';
people(11,'Tom');//单独调用的时候,this默认的是window 输出:window cato:none age:11,name:Tom
var man = {
cato:'man'
}
people.call(man,20,'Bob');// 这个时候把people绑定在man上,输出:{cato: "man"} cato:man age:20,name:Bob
people.apply(man,[30,'isak']);//输出:{cato: "man"} cato:man age:30,name:isak
因为apply可以把数组元素迭代为函数参数,所以在很多地方有妙用。
Math.max()用在数组上
let answer = Math.max.apply(null, [2, 4, 3])
console.log(answer) // 4
// 注意下面三个等价
Math.max.apply(null, [2, 4, 3])
Math.max.call(null, 2, 4, 3)
Math.max(2, 4, 3)
转自:https://juejin.cn/post/6922800009620815885
合并两个数组
// 将第二个数组融合进第一个数组
// 相当于 arr1.push('celery', 'beetroot');
let arr1 = ['parsnip', 'potato']
let arr2 = ['celery', 'beetroot']
arr1.push.apply(arr1, arr2)
// 注意!!!this的意思是要指定调用了push这个方法
// 所以当 this = arr1 后
// 就成了 arr1 调用了 push方法
// 上述表达式等价于 arr1.push('celery', 'beetroot')
console.log(arr1)
// ['parsnip', 'potato', 'celery', 'beetroot']
//以下三个语句完全等价
Array.prototype.push.apply(arr1, arr2)
arr1.push.apply(arr1, arr2)
arr2.push.apply(arr1, arr2)
转自:https://juejin.cn/post/6922800009620815885
2.bind call、apply是立即执行的,而bind是返回一个新函数,调用新函数的时候再执行。
function people() {
console.log(this)
}
let Tom = {
name:'Tom',
age:14
}
let foo = people.bind(Tom);
foo();
测试题
// 情况1
function foo() {
console.log(this.a) //1
}
var a = 1
foo()
// 情况2
function fn(){
console.log(this);
}
var obj={fn:fn};
obj.fn(); //this->obj
// 情况3
function CreateJsPerson(name,age){
//this是当前类的一个实例p1
this.name=name; //=>p1.name=name
this.age=age; //=>p1.age=age
}
var p1=new CreateJsPerson("尹华芝",48);
// 情况4
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
// 情况5
<button id="btn1">箭头函数this</button>
<script type="text/javascript">
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function () {
btn1.onclick = () => {
console.log(this);//obj
};
}
};
obj.getName();
</script>
Arguments 对象
arguments 是一个对应于传递给函数的参数的类数组对象。它只具有length和所以属性。
function fn(a,b,c){
console.log(arguments[0]);//a
console.log(arguments[1]);//b
}
把arguments转化成数组
// ES5
var arg1 = Array.prototype.slice.call(arguments);
var arg2 = [].slice.call(arguments);
// ES6
var arg3 = Array.from(arguments);
var arg4 = [...arguments];
链接:https://juejin.cn/post/6893642486556655630
手写call apply bind
1.手写call
首先要明确call的功能是对函数进行绑定,即要先原型上即 Function.prototype 上编程
1)拿到函数的引用this(判断是否是函数)
2)拿到传入的对象content(判断是否是null)
3)content.fn = this
4)获取传入的参数args
5)执行content.fn(...args)
6)删除函数fn,返回结果
//this指向调用mycall的那个函数
//content表示传入的对象
Function.prototype.myCall = function(content){
//先判断调用者是否是函数
if(typeof this !== 'function'){
throw new TypeError("调用者不是一个有效函数");
}
//不传参默认为window
content = content || window;
//给对象新增一个属性fn 并且指向要调用的函数
content.fn = this;
//把传入的参数(第二个以及第二个以后的)转化成数组
const args = Array.from(arguments).slice(1);
//对象调用函数
const result = content.fn(...args);
//删除函数
delete content.fn;
return result;
}
function people(age){
console.log(this.name + " " + age);
}
let Tom = {
name:'Tom'
}
people.myCall(Tom,12);//Tom 12
2.手写apply
apply的功能是对函数进行绑定,即要先原型上即 Function.prototype 上编程,它和call的主要区别在于传入的参数是一个数组,所以只需修改上call部分的3)
Function.prototype.myApply = function(content){
//先判断调用者是否是函数
if(typeof this !== 'function'){
throw new TypeError("调用者不是一个有效函数");
}
//不传参默认为window
content = content || window;
//给对象新增一个属性fn 并且指向要调用的函数
content.fn = this;
let result;
if(arguments[1]){//如果存在参数就是arguments[1]-->参数数组
result = content.fn(...arguments[1]);
}else{
result = content.fn();
}
//删除函数
delete content.fn;
return result;
}
function people(age1,age2,age3){
console.log(this.name + " " + age1 + " " + age2 + " " + age3);
}
let Tom = {
name:'Tom'
}
people.myApply(Tom,[12,13,14]);//Tom 12 12 14
3.手写bind
bind的功能是对原函数this绑定后返回一个新函数,要先原型上即 Function.prototype 上编程
1)拿到函数的引用this(判断是否是函数)
2)content.fn = this
3)获取传入的参数args
4)返回一个新函数result
5)绑定原型链 result.prototype = Object.create(fn.prototype);
Function.prototype.myBind = function(context){
//判断函数
if(typeof this !== 'function'){
throw new TypeError('Error');
}
// fn指向原函数
const fn = this;
//拿到参数
const args = Array.from(arguments).slice(1);
//返回一个新函数
const result = function(){
const resultArg = [...arguments];
if(this instanceof result){//如果是通过new调用的,绑定this为实例对象
fn.apply(this,args.concat(resultArg));
}else{//普通函数形式绑定
fn.apply(context,args.concat(resultArg));
}
}
//绑定原型链
result.prototype = Object.create(fn.prototype);
return result;
}
function print(){
console.log(this.name);
}
let people = {
name:"Lily"
}
let F = print.myBind(people,1,2,3);
F();//Lily