函数this的三种指向
环境对象 this:谁调用我,this就指向谁
说人话:this就相当于中文中的我,谁说出来这个字,这个字就代表谁
this指向取决于函数的调用,函数有三种调用函数
(1)普通函数:函数名() this->window
(2)构造函数:new 函数名()this->new创建的实例对象
(3)对象方法:对象名.方法名() this->对象
一句话总结:this指向三选一,先找new,后找点
function fn(){
console.log(this);
}
//(1)普通函数
fn//this指向window
//(2)构造函数
new fn()//this->new创建的实例
let obj = {
name : '张三',
eat :fn
}
// (3)对象方法
obj.eat()//this->对象
<script>
测试题
/*
环境对象 this :
普通函数;
对象方法:
构造函数;
*/
//作用域链
let obj = {
name: "张三",
eat: function() {
console.log(this)
function fn() {
console.log(this)
}
fn()
}
}
let eat = obj.eat
obj.eat()
</script>
2.函数的上下文调用
1.环境对象 this : 谁调用我,我就指向谁
普通函数; 函数名()this->window
对象方法: 对象名.方法名()this->对象
构造函数; new 函数名() this->new创建的实例对象
***默认情况下,函数内的this是固定的,无法被修改
***如果你想要动态的修改函数内部的this指向,则需要使用上下文调用方法
*上下文:函数作用域
*上下文指向:修改函数作用域内部this指向
2.上下文调用 : 修改函数内部this
2.1 函数名.call()
2.2 函数名.apply()
2.3 函数名.bind()
2.1函数名.call(修改的this,参数1,参数2.。。。)
function fn(a,b){
console.log(this);
console.log(a+b);
}
fn()
fn(10,20)
//函数名.call(修改的This,参数1,参数2)
fn.call({name:'张三'},10,20) //值传给this,
使用场景 万能检测数据类型
1.typeof数据:检测数据类型,但是有两种数据类型无法检测 *type无法检测 数组与null ,得到的都是‘object’
2.万能数据类型检测:Object.prototype.toString.call(数据) 最后得到的是 【object 数据类型】
** 原理**
// 万能的数据类型检测原理
// (1)Object.prototype.toString()内部会返回this的数据类型,得到固定格式字符串‘[object 数据类型 ]’
// (2)使用object原型中的toString()要想得到数据类型,只需要把this修改成你想要检测的对象
2.2函数名.apply(修改的this,数组/伪数组)
//只能传参为数组,或者伪数组,否则会报错
//apply会自动遍历数组和伪数组,然后逐一传参
fn.apply({name:"李四"},[50,60])
使用场景
2.2.1伪数组转真数组
<script>
/*
apply()场景 :伪数组转真数组
1.伪数组:有 数组三要素(下标、元素、长度)但是不能使用数组的方法
伪数组本质是 对象
*/
let obj = {
0 :10,
1:20,
2:30,
length:3
}
// 需求:有时候伪数组 想要使用 真数组的方法 ,就需要把伪数组转成真数组
// (1)把伪数组的元素取出来,push到真数组中
let arr = []
// arr.push(obj[0],obj[1],obj[2])
// console.log(arr);
// (2)收到写循环遍历数组
for(let i = 0;i<obj.length;i++){
arr.push(obj[i])
}
// (3)arr.push.apply(arr,伪数组)
// 这里使用apply不是为了修改this,而是借助传参特点:自动遍历伪数组/数组传参,所以第一个参数应该写arr(保持this不变)
// arr.push.apply(arr,obj)
// console.log(arr)
arr.push.apply(arr,obj)
console.log(arr);
// ES6:伪数组转真数组,固定静态方法 Array.from(伪数组)
**let newArr = Array.from(obj)
console.log(newArr)**
</script>
2.2.1伪数组转真数组 求数组的最大值
<script>
/*
apply场景:求数组的最大值
*/
let arr = [10,20,3040,50]
//(1)js基础
let max = arr[0]
for(let i = 0;i <arr.length;i++){
if(arr[i]>max){
max = arr[i]
}
}
console.log(max);
// (2)js高级:Math.max()
// let max1 = Math.max(arr[0],arr[1],arr[2],arr[3])
let max1 = Math.max.apply(Math,arr)
console.log(max1);
// (3)ES6:Math.max(...arr)
let max2 = Math.max(...arr)
console.log(max2);
2.3函数名.bind(修改的this)
blind()不会立即执行函数,而是得到修改this的新函数
*blind()一般修改:定时器函数、事件处理函数
//(3)函数.blind(修改的this)
// blind不会立即执行函数,而是得到一个修改this之后的新函数
let newFn = fn.blind({name:'王五'})//这里不能传参,因为会把参数定死
newFn(20,30)
3. call 和 apply 和 bind三者区别
相同点:都可以修改this指向
不同点:
(1)传参方式不同:call是单个传参,apply是数组/伪数组传参
(2)执行机制不同:call和apply会立即执行函数,bind不会立即执行而是得到修改this的新函数(一般修改定时器函数)
4.递归(在函数中调用自己)
递归类似于循环,也要有结束的条件
<script>
/*
1.递归函数:在函数中调用自己
*递归类似于循环,也要有结束的条件
2.递归应用:
2.1浅拷贝与深拷贝
2.2遍历dom树
*/
function fn(){
console.log('哈哈');
fn()
}
fn()
// 双函数递归
function fn1(){
console.log('呵呵');
fn2()
}
function fn2(){
console.log('嘿嘿');
fn1()
}
fn1()
</script>
应用场景
4.1.浅拷贝与深拷贝
3.浅拷贝与深拷贝 有两种实现方式
3.1浅拷贝:拷贝地址,修改拷贝后的数据,对原数据有影响
3.2深拷贝:拷贝数据,修改拷贝后的数据,对原数据没有影响
深拷贝两种方式:(1)json方式:let newObj = JSON.parse(JSON.stringify(js对象))
(2)递归函数
浅拷贝:拷贝地址
// let newObj = obj
深拷贝:json深拷贝
把所有的数据都拷贝一份,如果里面有嵌套的内容,也会被拷贝一份,比如hobby
(1)JSON.stringify(js对象):把js对象->json字符串(json底层会自动深拷贝)
let json = JSON.stringify(obj)
(2)JSON.parse(json字符串):json字符串->js对象
let newObj = JSON.parse(json)
*/
/* let newObj = JSON.parse(JSON.stringify(obj))
newObj.name = '常常'
console.log(obj,newObj); */
// 2.深拷贝函数封装
function kaobei(obj,newObj){
// 遍历obj,把obj里面的数据 拷贝给newObj
for(let key in obj){
// 判断是不是数组,如果是数组还需要继续遍历拷贝
if(obj[key] instanceof Array){
// (1)声明空数组
newObj[key] = []
// (2)递归遍历数组
kaobei(obj[key],newObj[key])
}else if(obj[key] instanceof Object){
// (1)声明空对象
newObj[key] = {}
// (2)递归遍历数组
kaobei(obj[key],newObj[key])
}else {
newObj[key] = obj[key]
}
}
}
// (1)创建一个空对象
let newObj = {}
// (2)调用拷贝函数
kaobei(obj,newObj)
newObj.name = '李四'
console.log(obj,newObj);
5.闭包
1.闭包closure是什么
两个条件 a:函数 b:函数内部还要访问 其他函数的变量
(1)闭包是一个 访问其他函数内部变量 的函数
(2)闭包 = 函数+上下文引用
2.闭包作用:解决变量污染
*实际开发中,闭包一般用于回调函数
let age = 20
function fn(){
// 局部变量
let num = 0
// fn1+num的组合就是闭包
function fn1(){
console.log(num);
console.log(age);//age不能组成闭包
}
fn1()
}
fn()
3.在浏览器中调试闭包
document.querySelector('.btn').addEventListener('click', function () {
// (1)获取输入框文本
let text = document.querySelector('input').value
// (2)模拟网络请求
setTimeout(function () {
alert(`${text}的搜索结果为123456`)
}, 1000)
})