函数的上下文

news/2024/10/6 15:35:58

函数的上下文

概述

在函数体的语句中,会出现this这个词,this就是函数的上下文

函数中this是谁,就说明函数的上下文是谁

函数中的this是谁,要看是如何调用的,因为this不是一成不变的

比如我们看下面的例子

var obj = {a: 100,fun: function() {console.log(this.a);}
};

我们此时在obj对象中定义了一个fun函数,就是obj的属性

现在如果直接对象打点调用

obj.fun();

此时会弹出100,说明上下文就是对象本身

如果此时我们将整个方法进行一次赋值

var obj = {a: 100,fun: function() {console.log(this.a);}
};
var f = obj.fun;
f();

页面中就会弹出undefined,因为此时this的上下文不是obj了,而是window

规则1:直接圆括号执行,上下文是window对象

什么叫做直接圆括号调用,就是没有对象打点执行,不是方括号枚举执行,通常是从数组、对象中提取出来后单独执行的

var obj = {a: 100,fun: function() {alert(this.a);}
};
var f = obj.fun;
f();

f()就是直接圆括号执行,因为这个f是从obj提取出来的

  • 直接圆括号执行的this指向的是window对象
  • 需要注意的是js中全局变量都是window对象的属性
  • 还需要注意的是IIFE也属于直接圆括号调用的范畴,里面的this都是window对象
var a = 300;
var obj = {a: 100,b: (function() {console.log(this.a)})()
};

弹出的内容是300,一位IIFE的this指向的是window

小题目

企业面试题

var xiaohong = {name: '小红',age: 25,sayHello: (function() {console.log(this.age)return this.age >= 18 ? '女士' : '女生'})()
}
console.log(`大家好,我叫${xiaohong.name},我是一个${xiaohong.sayHello}`);

答案是女生,这道题的重点是IIFE里面的this,上面我们说过了IIFE里面的this指向的是window,所以此时IIFE里面的this.ageundefined,由于undefined >= 18结果是false,三元表达式走后面的“女生”

小题目

var obj = {a: 100,fun: function() {var a = 200;console.log(this.a);}
}
var a = 300;
var f = obj.fun;
f();

答案是300,切记this指向谁一定要看调用,此时我们发现,调用是圆括号直接执行的。所以我们就知道了,内部的this就是window,所以obj里面的所有的a都是障眼法。由于全局变量都是window对象的属性,所以var a = 300就是window.a = 300,此时弹出的结果就是300

规则2:从对象中调用或者数组中枚举执行的函数,上下文就是这个对象或者数组

先补充点函数知识

函数的length指的是函数的形参列表长度

function fun(a, b, c, d, e, f) {}
console.log(fun.length);

函数的实参是一个arguments对象

function fun(a, b, c, d, e, f) {console.log(arguments)
}
fun(1, 2, 3, '你好', '哈哈');

每一个函数都有一个属性是arguments,值是一个类数组对象

什么是类数组对象?

类数组对象和数组很像,本质是对象,拥有数组的length属性,有对应的下标索引值。函数的arguments或者我们document.getXX获取DOM的时候返回对象类型都是类数组对象,因为这些对象虽然看似数组,但是没有数组的能力,不能进行push等等操作

我们知道函数中this是上下文,需要看如何调用,如果想表达函数自己,用arguments.callee

function fun() {console.log(arguments.callee == fun)
}
fun();

小题目

function fun1(a, b, c) {arguments[0]();
}function fun2(a, b, c, d, e) {console.log(this.length);
}fun1(fun2, 9, 2, 4, 2, 34, 234);

此时this是看谁调用的。fun1在调用时,fun1调用的时候执行了函数arguments[0],因为argumentsfun1的实参列表,所以第0项就是fun2函数,所以符合规则2;fun2函数中的this指的就是fun1函数的arguments类数组对象,所以length就是7

小题目

此时我们把上面的题目升级

function fun1(a, b, c) {arguments[0](1, 2, 3, 4, 5, 6);
}
function fun2(a, b, c, d, e) {console.log(this.length);console.log(arguments.length);console.log(arguments.callee.length);console.log(this.callee.length);
}
fun1(fun2, 9, 2, 4, 2, 34, 234);

解析:通过分析知道了fun2中的this指的是fun1函数,所以此时this.length指的就是fun1arguments类数组对象(因为是类数组枚举执行的符合规则2)

arguments本身是fun2函数自己的实参列表,所以长度是6(调用的时候传了1~6的参数)

我们知道arguments.calleefun2函数自己,所以length就是形参列表为5

this.callee.length指的就是fun1的形参列表为3

小题目

var m = 2;
var obj = {fun1: function() {return this.fun2();},fun2: fun2,m: 4
};
function fun2() {return this.m;
}
console.log(obj.fun1());

题目的核心就是上下文的传递

小题目

综合前面规则1和规则2出的面试题

var num = 1;
var obj = {num: 2,fun: (function() {var num = 3;this.num += 4;return function() {this.num *= 5;num *= 6;console.log(num);}})()
};
obj.fun();
obj.fun();
console.log(num);
console.log(obj.num);
var f1 = obj.fun;
f1();
console.log(num);
console.log(obj.num);
var f2 = obj.fun;
f2();
console.log(num);

小题目

var length = 1;
var obj = {length: 10,b: [{length: 20,fun: function() {console.log(this.length);}}]
};
var arr = [obj, obj.b, obj.b[0], obj.b[0].fun];
arr[0].b[0].fun();
arr[1][0].fun();
arr[2].fun();
arr[3]();

规则3:定时器直接调用,上下文是window对象

var a = 100;
function fun() {console.log(this.a++);
}
setInterval(fun, 1000);

需要注意的是定时器调用和定时器内部调用是有区别的

下面代码是定时器在调用obj.fun函数,所以调用者是定时器

var obj = {a: 300,fun: function() {console.log(this.a++);}
}
var a = 100;
setInterval(obj.fun, 1000);

下面的代码本质是obj在调用函数,所以上下文是obj

var obj = {a: 300,fun: function() {console.log(this.a++);}
}
var a = 100;
setInterval(function() {obj.fun();
}, 1000);

规则4:DOM事件中的this,指的是触发事件的这个DOM元素

// 首先生成四个div
for (var i = 0; i < 4; i++) {var div = document.createElement('div');div.style.width = '100px';div.style.height = '100px';div.style.backgroundColor = 'white';div.style.display = 'inline-block';div.style.marginRight = '16px';div.style.border = '1px solid black';div.id = ('box' + i);document.body.appendChild(div);
}
// 点击div修改颜色
var box0 = document.getElementById('box0');
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = document.getElementById('box3');function changeColor() {this.style.backgroundColor = 'purple';
}
box0.onclick = changeColor;
box1.onclick = changeColor;
box2.onclick = changeColor;
box3.onclick = changeColor;

规则5:call()apply()可以设置函数的上下文

函数的上下文主要是看谁在调用,但是我们可以通过call()apply()区设置函数的上下文

call()apply()本质就是调用函数的同时,指定上下文

比如我们有一个changeSex的函数,它的作用是修改sex的属性

此时有一个xiaohong对象,sex为女

此时我们调用这个changeSex函数,强行将函数的上下文绑定为xiaohong

function changeSex() {if (this.sex == '男') {this.sex = '女';} else {this.sex = '男';}console.log(this);
}
var xiaohong = {name: '小红',sex: '女'
}
changeSex.call(xiaohong);
console.log(xiaohong.sex);

此时小红对象被修改为了男

apply函数也有同样的功能

changeSex.apply(xiaohong);

规范

函数.call(带有上下文的内容);
函数.apply(带有上下文的内容);

函数的上下文就是带有上下文的内容

需要注意的是call()apply()的本质核心是有区别的:主要是语法上的区别。call是接收参数,apply是接收数组

call方法要求,所有的参数在上下文对象后面一一罗列

function person(name, age, height, weight) {this.name = name;this.age = age;this.height = height;this.weight = weight;
};
var xiaoming = {name: '小明',age: 3,height: 60,weight: 15
};
person.call(xiaoming, '小明', 23, 183, 65);

此时我们换成applyapply要求所有的参数必须规整到一个数组中

person.apply(xiaoming, ['小明', 30, 183, 75]);

apply本质上只要求两个参数,第二个参数是一个数组集合

在使用结果上两种方式都是一样的

小题目

function fun1() {fun2.apply(obj, arguments)
}
function fun2(a, b, c) {console.log(obj);console.log(a);console.log(b);console.log(c);
}
var obj = {name: '小明',sex: '男'
}
fun1('香蕉', '葡萄', '梨子');

此时你会发现apply有一个功能是将第二个数组参数进行解构,变成一个个的罗列参数,比如我们传进arguments是一个类数组对象,但是我们在fun2函数接收的a,b,c形参中进行了解构,也就是分别变成了 香蕉、葡萄、梨子

小题目

此时我们想根据apply的特点出一个思考题,利用Math方法进行数组的最大值查找

Math.max.apply(null, [789, 2342, 123, 2134, 2345, 22])

我们知道apply第二个参数是数组,但是apply有能力给解构,所以我们可以利用这个特点求数组的最大或最小值

上面的题目是企业中经常遇到的面试题,一定不要只写循环遍历求最大最小值

企业面试题

题目1

var a = 1;
function fn() {this.a++;a += 10;var a = 8;
}
fn()
console.log(a)

答:结果是2,因为fn执行的时候内部的this指的是window也就是全局的a=1,所以this.a++等于2;函数内部的a += 10本质上是undefined += 10结果为NaN,因为后面有个var a = 8;因为变量声明提升的原因造成的

题目2

var length = 5;
var arr = [fn1, fn2];
function fn1() {return this.length;
}
function fn2() {return this[0];
}
var a = arr[0]();
var b = arr[1]()();
console.log(a);		
console.log(b);		

因为a此时的this是数组枚举执行,符合规则2,也就是上下文是arr数组,所以length是2

b其实最后返回的就是一个函数,函数直接圆括号执行此时上下文是window所以是5

题目3

var number = 2;
var obj = {number: 3,fn1: (function() {this.number *= 2;number = number * 3;var number = 2;return function() {this.number *= 4;number *= 5;console.log(number);}})(),fn2: function() {this.number *= 2;}
}
var fn1 = obj.fn1;
console.log(number);
fn1();
obj.fn1();
obj.fn2();
console.log(window.number);
console.log(obj.number);

题目4

var length = 5;
function getLength() {return this.length;
}
function foo() {this.length = 1;return (function() {var length = 2;return {length: function(a, b, c) {return this.arr.length;},arr: [1, 2, 3, 4],info: function() {return getLength.call(this.length);}}})();
}
var result = foo().info();
console.log(result);

答案是3,因为result是一个对象,这个对象打点调用infoinfo函数中执行了getLength函数,并且将上下文一同绑定了,绑定的是对象的length属性,该属性值是一个函数,所以getLength函数在执行时候的this就是函数,this.length也就是该函数的length结果为3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/68338.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

拥挤聚集智能监测系统

拥挤聚集智能监测系统可以通过对人员数量、密度等进行实时监测,拥挤聚集智能监测系统识别出拥挤聚集的情况,并及时发出预警。拥挤聚集智能监测系统可以通过对人员进车间的人数等进行监测,识别出是否存在人员拥堵、挤压等安全隐患,及时发出警报,提醒工作人员采取措施疏散人…

睡岗识别 AI助力企业安全管控

睡岗识别可以通过AI视频智能分析技术,睡岗识别识别出操作人员是否存在睡岗情况。例如,在变电站等场景中,睡岗识别技术可以通过对识别出操作人员是否存在睡岗情况,及时发出预警,避免因操作人员的疏忽而导致的安全事故。在工厂车间中,睡岗识别技术可以通过对工人的行为进行…

加油站安全风险监测预警系统

加油站安全风险监测预警系统可以通过对加油站设备、环境、人员等方面进行监测,加油站安全风险监测预警系统实现对加油站的全面监管。例如,在加油站油罐区中,加油站安全风险监测预警系统可以对加油站人员抽烟打电话、明火烟雾等环境安全隐患进行自动识别,及时发出预警,避免…

山西煤矿电子封条

山西煤矿电子封条通过AI视觉分析技术,山西煤矿电子封条实现对各矿区(煤矿和非煤矿区)每日矿井出入井人监察控制、调度室空岗识别、煤矿生产作业状态、摄像头遮挡、挪动角度识别、货运车辆出矿识别等。山西煤矿电子封条实现当前待办事项的推送,以及对各矿区用户区域内的报警…

离岗识别 AI助力企业安全管控

离岗识别通过yolov5网络模型技术,离岗识别可以自动识别办公室、工厂、监控室监控画面中人员离岗脱岗睡岗等行为,发现违规行为立即抓拍告警并同步睡岗离岗等违规数据到后台提醒值班人员及时处理。离岗识别采用人工智能算法识别技术对各主控室、办公室、工厂、煤矿监控室等人员…

登高作业安全带穿戴识别系统 - 保障登高作业人员安全

登高作业安全带穿戴识别系统是一种通过Ai视觉智能分析技术,登高作业安全带穿戴识别系统实现对登高作业人员是否穿戴安全带进行监测的系统,登高作业安全带穿戴识别系统通过视频监控智能识别技术检测登高作业人员是否佩戴安全带,并及时发出警报,以提醒工作人员及时穿戴安全带…

MySQL单表存多大的数据量比较合适

前言 经常使用MySQL数据库的小伙伴都知道,当单表数据量达到一定的规模以后,查询性能就会显著降低。因此,当单表数据量过大时,我们往往要考虑进行分库分表。那么如何计算单表存储多大的数据量合适?当单表数据达到多大的规模时,我们才要进行分库分表呢? MySQL存储方式 首先…

【THM】kiba练习

脚本小子是这样的,黑客只要写POC就可以,可是脚本小子要考虑的事情就多了。 学到了新知识:利用网上的POC进行复现、利用Capabilities进行提权【THM】kiba练习 与本文相关的TryHackMe实验房间链接:TryHackMe | kiba 简介:识别数据可视化仪表板中允许执行远程代码执行的关键…