首页 > 其他分享 >函数的上下文

函数的上下文

时间:2024-10-06 15:33:22浏览次数:8  
标签:function console 函数 fun var obj 上下文 log

函数的上下文

概述

在函数体的语句中,会出现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

标签:function,console,函数,fun,var,obj,上下文,log
From: https://www.cnblogs.com/hellozjf/p/18449107

相关文章

  • sort函数详解
    sort函数简介其实STL中的sort()并非只是普通的快速排序,除了对普通的快速排序进行优化,它还结合了插入排序和堆排序。根据不同的数量级别以及不同情况,能自动选用合适的排序方法。当数据量较大时采用快速排序,分段递归。一旦分段后的数据量小于某个阀值,为避免递归调用带来过大的额外......
  • 用自定义函数解决帝国cms的简介截取字符时出现html的问题
    帝国CMS在截取文章简介时出现HTML标签的问题可以通过自定义函数来解决。具体步骤如下:步骤1:自定义函数 NoHTML()打开 connect.php 文件找到 e/class/connect.php 文件并打开。添加自定义函数 NoHTML()在文件中添加以下函数://去除HTML标记function......
  • llama.cpp推理流程和常用函数介绍
    llama.cpp是一个高性能的CPU/GPU大语言模型推理框架,适用于消费级设备或边缘设备。开发者可以通过工具将各类开源大语言模型转换并量化成gguf格式的文件,然后通过llama.cpp实现本地推理。经过我的调研,相比较其它大模型落地方案,中小型研发企业使用llama.cpp可能是唯一的产品落地方案......
  • 在Js中匿名函数的几种写法
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document</title><......
  • 数组和函数实践:扫雷游戏
    ⽬录1.扫雷游戏分析和设计2.扫雷游戏的代码实现3.扫雷游戏的扩展1.扫雷游戏分析和设计1.1扫雷游戏的功能说明•使⽤控制台实现经典的扫雷游戏•游戏可以通过菜单实现继续玩或者退出游戏•扫雷的棋盘是9*9的格⼦•默认随机布置10个雷•可以排查雷   ......
  • 字符函数和字符串函数和内存函数
    strtok函数原型与功能概述在C语言中,strtok函数的原型为char*strtok(char*str,constchar*delim)。它的主要功能是将字符串str按照delim中指定的分隔符进行分割,返回被分割出来的子字符串。工作原理首次调用:当第一次调用strtok时,需要将待分割的字符串str作为第一个参数传入。st......
  • 目录工具类 - C#小函数类推荐
          此文记录的是目录工具类。/***目录工具类AustinLiu刘恒辉ProjectManagerandSoftwareDesignerE-Mail:[email protected]:http://lzhdim.cnblogs.comDate:2024-01-1515:18:00***/namespaceLzhdim.LPF.Utility......
  • 三角函数:基础知识&&Omega范围问题
    三角函数:基础知识&&Omega范围问题说是高考热门,其实也没怎么考过(我们知道,高中主要研究的三个三角函数的一般形式分别为:\(A\sin(\omegax+\varphi)+h\),\(A\cos(\omegax+\varphi)+h\),\(A\tan(\omegax+\varphi)+h\)。\(h\)由于作用太low啦作用不大,高中一般不予讨论,所......
  • [题解]P7077 [CSP-S2020] 函数调用
    P7077[CSP-S2020]函数调用题意简述给定一个长度为\(n\)的序列\(a_1,a_2,\dots,a_n\),给定\(m\)个函数,每个函数可能是下面\(3\)种类型,用\(T_x\)表示函数\(x\)的类型:\(T_x=1\),对下标\(p\)增加\(v\)。\(T_x=2\),对所有元素乘\(v\)。\(T_x=3\),由若干类型\(1\)和类型\(2\)组成......
  • const和readonly修饰的成员,静态构造函数以及对于变量的访问{get;set}
    第一,const修饰的数据类型定义:按照publicconstinttest=20;的格式进行声明,const修饰的数据类型叫做常量。注意:1访问时只能通过类名加变量名访问。      2必须在声明的时候就赋值。      3常量是不可修改的值。代码如下:usingSystem.Collection......