脚本宝典收集整理的这篇文章主要介绍了函数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>
函数上下文的三个方法: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()语法并不会立即执行函数,而是返回一个修改指向后的新函数,常用于回调函数
<!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:谁调用我,我就指向谁
1.全局函数:this指向window
2.对象方法:this指向对象
3.构造函数:this指向new创建的空对象
相同点:都是修改函数this指向
不同点:
传参方式不同:call用于单个参数,apply用于多个参数(数组/伪数组)
执行机制不同:call与apply会立即执行,bind不会立即执行
call、apply用一次修改一次
bind:依次修改,终生有效
什么是闭包:以下两种回答都可以
闭包是一个访问其他函数内部变量的函数
闭包是函数+上下文代码组合
闭包作用:解决变量污染
什么是递归:函数内部调用自己
递归场景
深拷贝
遍历dom树
以上是脚本宝典为你收集整理的函数this三种指向、函数调用上下文模式、递归、闭包全部内容,希望文章能够帮你解决函数this三种指向、函数调用上下文模式、递归、闭包所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。