函数this三种指向函数调用上下文模式递归闭包
Posted cs1330
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数this三种指向函数调用上下文模式递归闭包相关的知识,希望对你有一定的参考价值。
函数的三种调用方式(this指向)
函数三种调用方式:普通函数 对象方法 构造函数
重点:理解this关键字的作用:谁调用这个函数,this就指向谁
无法修改this指向的三种:
1.普通函数调用:this指向的就是window
2.对象方法调用:this指向的就是这个对象
3.构造函数调用:this指向的就是new关键字创造的对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
函数三种执行模式 : 全局函数 、 对象方法 、 构造函数
this : 谁 `调用` 我,我就指向谁
1. 全局函数 : this指向window
2. 对象方法 : this指向对象
3. 构造函数 : this指向new创建的空对象
*/
// this环境对象:谁调用我,我就指向谁
// 普通函数调用 this指向的就是window
function fn()
console.log(this);
fn()
// 对象方法调用:对象名.方法名() this指向就是这个对象
let obj =
name: '陈爽',
age: 19,
saiHia: function ()
console.log(this);
obj.saiHia()
// 构造函数调用:new 函数名() this指向new 创建的实例对象
function Fn()
console.log(this);
let s = new Fn()
</script>
</body>
</html>
函数调用的上下文模式(可以修改函数中this指向)
函数上下文的三个方法:call() 、 apply() 、 bind() ,他们定义在Function构造函数中
函数执行上下文模
作用可以修改this指向
异同点:都可以修改函数中的this指向
不同点:传参方式不同
call:函数名.call(this修改后的指向,参数1,参数2....)
适用于只有一个参数的函数
应用场景:伪数组排序、检测数据类型
// call可以修改this指向
// 语法:函数名.call(修改的this,参数1,参数2)
function fn(a, b)
console.log(this);
console.log(a + b);
fn.call( name: '张子沁' , 30, 20)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 检测数据类型固定格式语法:object.prototype.toString.call(数据)
console.log(Object.prototype.toString.call('123'));
console.log(Object.prototype.toString.call(12312));
console.log(Object.prototype.toString.call(true));
console.log(Object.prototype.toString.call(undefined));
console.log(Object.prototype.toString.call(null));
console.log(Object.prototype.toString.call([10, 20, 30]));
console.log(Object.prototype.toString.call(function () ));
console.log(Object.prototype.toString.call( name: '张三' ));
</script>
</body>
</html>
apply() :函数名:apply(this修改后的指向,伪数组/数组)
适用于多个传参的函数
应用场景:伪数转真数组、求数组最大值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// apply上下文调用:修改this指向 传参不同
// 语法:函数名.apply(修改的this指向,数组/伪数组)
function fn(a, b, c)
console.log(this);
console.log(a * b + c);
fn.apply( name: '陈爽' , [30, 50, 50])
</script>
</body>
</html>
伪数组转真数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj =
0: 20,
1: 30,
2: 40,
3: 90,
4: 100,
length: 5
// console.log(obj);
// 将伪数组转为真数组
// ES6语法
// let arr = Array.from(obj)
// console.log(arr);
// ES5语法
// 创建一个空数组
let Array = []
// 函数名.apply(修改的this,数组/伪数组)
// 第一个参数:不用修改this,因为this本来就指向Array
// 第二个参数:obj,借助apply特点:自动遍历了伪数组,逐一传参
Array.push.apply(Array, obj)
console.log(Array);
</script>
</body>
</html>
求数组最大值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let arr = [20, 40, 90, 10, 32, 90]
// 排序法 // 最大值
// arr.sort(function (a, b)
// return b - a
// )
// console.log(arr[0]);
// ES5语法
// 第一个参数:Math,因为他this指向本身就Math,这里不用修改this指向,相当于不变
// 第二个参数:arr,借助apply的特点,自动遍历数组里的元素,然后逐一传参,求出最大值赋值给max
let max = Math.max.apply(Math, arr)
console.log(max);
// ES6语法
let max3 = Math.max(...arr)
console.log(max3);
</script>
</body>
</html>
bind()语法:函数名.bind(this修改后的指向,参数1,残数2)
bind()语法并不会立即执行函数,而是返回一个修改指向后的新函数,常用于回调函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
/*
1.函数三种执行方式 :
全局函数 : this指向window
对象方法: this指向对象
构造函数 : this指向new创建的对象
共同的特点: this的指向无法动态修改
2.函数上下文模式 :
2.1 作用: 可以动态修改函数中的this
2.2 语法: 有3种写法,作用都是一样改this,应用场景不同
a. 函数名.call(修改的this,arg1,arg2…………)
* 适用于函数原本形参 <= 1
b. 函数名.apply(修改的this,[数组或伪数组])
* 适用于函数原本形参 >= 2
c. 函数名.bind(修改的this,arg1,arg2…………)
* 特点:不会立即执行这个函数,而是返回修改this后的新函数
* 适用于不会立即执行的函数 : 事件处理函数,定时器函数
*/
// function fn()
// //三种执行模式this无法动态修改
// //this = age:18;
// console.log(this);
// ;
// fn();//this:window
/* 上下文模式 */
function fn(a,b)
console.log(this);
console.log(a+b);
;
//a. 函数名.call(修改的this,arg1,arg2…………)
//应用场景: 适用于函数原本形参 <= 1
fn(10,20);//this:window
fn.call(age:18,100,200);
//b. 函数名.apply(修改的this,[数组或伪数组])
//应用场景: 适用于函数原本形参 >=2
fn.apply(age:88,[20,30]);
//c. 函数名.bind(修改的this,arg1,arg2…………)
//特点:这个函数不会立即执行,而是返回一个修改this之后的新函数
//应用场景 : 事件处理函数,定时器
let newFn = fn.bind(name:'坤坤');
newFn(50,60);
//4. 定时器中的this一定是指向window
// 定时器:一段代码间隔事件执行 setTimeout(一段代码,间隔时间)
//4.1 具名函数
let test = function()
console.log('我是具名函数');
console.log(this);
;
let newTest = test.bind(name:'张三')
setTimeout(newTest,3000);
//4.2 匿名函数
setTimeout(function()
console.log('我是定时器中的函数');
console.log(this);
.bind(name:'李四'),2000);
</script>
</body>
</html>
//2.3 bind();
//语法: 函数名.bind(this修改后的指向,arg1,arg2....);
let obj =
name:"文聪兄"
;
function getSum(a,b)
console.log(this);
console.log(a+b);
getSum(10,20);//this指向window
let fn = getSum.bind(obj); //bind()不会执行这个函数,而是会返回一个修改了this的函数.
fn(100,200); //此时这个fn,就相当于是修改了this之后的getSum.
//3 回调函数(例如定时器),一般使用bind来修改this指向
let obj =
name:"班长"
;
//3.1 定时器的回调函数是一个具名函数
function test1()
console.log(this);
let test2 = test1.bind(obj);
setInterval(test2,2000);
//3.2 定时器的回调函数是一个匿名函数
setInterval(function ()
console.log ( this );
.bind(obj),2000);
递归
递归函数介绍
1.递归函数:一个函数自己调用自己
2.递归函数特点:
一定要有结束条件,否则会导致死循环
能够用递归函数实现的需求,就一定可以用循环调用函数来解决,只是代码简洁与性能不同而已
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
/*
1. 递归函数 : 在函数中自己调用自己
2. 递归特点
a. 能用递归实现的功能一定可以用循环,只是语法不同
b. 递归一定要有结束的条件,否则会导致死循环
*/
//一个函数递归
// function fn()
// console.log('哈哈');
// fn();
// ;
// fn();
//两个函数递归
// function fn1()
// console.log('哈哈');
// fn2();
// ;
// function fn2()
// console.log('呵呵');
// fn1();
// ;
// fn2();
//需求:写一个函数,打印三次 我爱坤哥
let i = 1;
function fn()
console.log('我爱坤哥');
i++;
if(i <= 3)
fn();
;
//循环实现
// for(let i = 1;i<=3;i++)
// console.log('我爱坤哥');
// ;
;
fn();
</script>
</body>
</html>
递归应有场景:浅拷贝与深拷贝
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<script>
/*浅拷贝与深拷贝概念主要针对于对象这种数据类型
1.浅拷贝: 拷贝的是地址
* 特点:修改拷贝后的数据,原数据也会随之修改
2.深拷贝:拷贝的是数据
* 特点:修改拷贝后的数据,对原数据没有影响
*/
let obj =
name: 'ikun',
age: 32,
hobby: ['讲课', '敲代码', '黑马程序员'],
class:
name: '武汉大前端',
salary: [18888, 12000, 10000]
//1.浅拷贝: 拷贝的是地址
let obj1 = obj
obj1.name = '黑马李宗盛'
console.log(obj, obj1)
//2.深拷贝:拷贝的是数据
//核心原理:使用递归。 只要遇到属性值是引用类型,则遍历。
function kaobei (newObj, obj)
// 遍历
for (let key in obj)
if (obj[key] instanceof Array)
// obj[key] 是数组
// obj[key]是数组
newObj[key] = []
kaobei(newObj[key], obj[key])
else if (obj[key] instanceof Object)
// obj[key] 是对象
// obj[key]再遍历拷贝
newObj[key] =
kaobei(newObj[key], obj[key])
else
newObj[key] = obj[key]
let obj2 =
//深拷贝
kaobei(obj2, obj)
//修改拷贝后的数据
obj2.name = '黑马颜值担当'
obj2.hobby = '唱歌'
console.log(obj,obj2)
</script>
</body>
</html>
递归应用场景:遍历dom树
-
在我们后期vue项目中,有一个这样的案例:服务器返回一个不确定的数据结构。 是一个
多级菜单
,这个数据是不确定的。我们需要根据服务器返回的数据,来生成对应的页面结构<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> <style> * padding: 0; margin: 0; .menu p width: 100px; border: 3px solid; margin: 5px; .menu > div p margin-left: 10px; border-color: red; .menu > div > div p margin-left: 20px; border-color: green; .menu > div > div > div p margin-left: 30px; border-color: yellow; </style> </head> <body> <div class="menu"> <!-- <div> <p>第一级菜单</p> <div> <p>第二级菜单</p> <div> <p>第三级菜单</p> </div> </div> </div> --> </div> <script> //服务器返回一个不确定的数据结构,涉及到多重数组嵌套 let arr = [ type: '电子产品', data: [ type: '手机', data: ['iPhone手机', '小米手机', '华为手机'] , type: '平板', data: ['iPad', '平板小米', '平板华为'] , type: '智能手表', data: [] ] , type: '生活家居', data: [ type: '沙发', data: ['真皮沙发', '布沙发'] , type: '椅子', data: ['餐椅', '电脑椅', '办公椅', '休闲椅'] , type: '桌子', data: ['办公桌'] ] , type: '零食', data: [ type: '水果', data: [] , type: '咖啡', data: ['雀巢咖啡'] ] ] /* 使用递归遍历数组 */ function addElement (arr, father) for (let i = 0; i < arr.length; i++) let div = document.createElement('div') div.innerHTML = `<p>$arr[i].type || arr[i] </p>` father.appendChild(div) if( arr[i].data ) addElement(arr[i].data,div) //调用递归函数 addElement(arr,document.querySelector('.menu')) </script> </body> </html>
闭包
1.闭包:是一个可以访问其他函数变量的函数
2.闭包作用:解决变量污染问题,让变量被函数保护起来
function fn()
let a = 1
function fn1()
console.log(a)
fn1()
执行函数 fn1
用到了另一个函数fn
中的 a
这个变量,所以 fn1 + a
构成了闭包。
闭包的案例
-
案例需求:在输入框输入搜索文字,点击百度一下按钮,用定时器模拟网络请求,1 秒之后显示搜索结果;
结构如下:
<div class="box">
<input type="search" name="" id="">
<button>百度一下</button>
</div>
代码如下:
// 1. 获取元素
let search = document.querySelector('.box input')
let btn = document.querySelector('.box button')
// 2. 添加点击事件
btn.onclick = function ()
// 获取搜索的文字
let text = search.value
// 模拟发送网络请求
setTimeout(function ()
alert(`您搜索的内容是 $text 共搜索到 12345 条结果`)
, 1000)
学习重点梳理(高频面试题)
this三种指向
this:谁调用我,我就指向谁
1.全局函数:this指向window
2.对象方法:this指向对象
3.构造函数:this指向new创建的空对象
call、apply、bind三者区别
相同点:都是修改函数this指向
不同点:
传参方式不同:call用于单个参数,apply用于多个参数(数组/伪数组)
执行机制不同:call与apply会立即执行,bind不会立即执行
call、apply用一次修改一次
bind:依次修改,终生有效
闭包
什么是闭包:以下两种回答都可以
闭包是一个访问其他函数内部变量的函数
闭包是函数+上下文代码组合
闭包作用:解决变量污染
递归
什么是递归:函数内部调用自己
递归场景
深拷贝
遍历dom树
以上是关于函数this三种指向函数调用上下文模式递归闭包的主要内容,如果未能解决你的问题,请参考以下文章