一、 arguments 与rest与 instanceof关键字
-
arguments:他是一个伪数组,他是用来获取所有的实参,他用于实参不确定的函数
-
rest剩余参数:他是一个真数组,他是用来获取函数剩余的所有实参
-
instanceof: 运算符 作用:检测构造函数的原型在不在实例对象的原型链中
二、 this的指向
1:this是什么
- this又叫环境对象,this是不确定,因为this是谁调用我,this就指向谁: 2:函数三种的调用方式
- 2.1 普通函数的调用:this的指向的是window
- 2.2 对象的调用:this的指向的是对象
- 2.3 构造函数的调用:this指向的是new创建的实例对象。 3:函数三种调用方式的共同点
- 3.1 函数的三种调用方式有一个共同点:就是this无法修改
三、函数的上下文调用 call,apply,bind
1:什么是函数的上下文调用
- 概念:可以理解为就是this的作用域 2:函数的上下文调用有什么用
- 作用:函数上下文调就是用于修改函数中的this的指向,他有三个方法call,apply,bind用来
方法1 call()
一 语法格式: 函数名.call(修改this,参数1,参数2)
<script>
function fn(a,b){
console.log(this);
console.log(a+b);
}
fn(10,20)
// 上下文调用:函数.call(修改this,形参1,形参2)
fn.call({name:'小明'},20,30)
</script>
二 call的经典应用场景。
1:call最重要的应用在数据检测
-
以前我们检测数据类型,常用的是typeof,但是他有一个缺陷,typeof检测null和Array是检测不出来的,一律显示Object 2:万能检测数据法
-
1:每一个对象都有一个toString方法,如果这个toString未被覆盖,他会返回一个固定的格式,
[object type],其中type就是对象的类型,所以用toString方法来检测数据类型,因为万物都是对象,但是根据原型链的就近查找原则,他首先不会去找原型Object,而是先查找自身是否有toString方法,但是数组也有toString方法,所以他找不到Objec的toString那里去,但只有Object的toString方法才能检测数据。 -
2: 所以要指定是哪个原型身上的toString,Object.prototype.toString才可以,但
-
3: Object.prototype.toString去检测,但this的指向始终执行这个对象的方法所以需要通过call来修改他的this,把this改成指向我要检测数据类型的值
let num=10
let str='10'
Object.prototype.toString.call(str)
Object.prototype.toString.call(num)
方法2 apply()
语法格式:函数名.apply(修改this,数组/伪数组)
- apply()跟call()方法,他的不同就是传参的方式,他是传数组或者伪数组。他适用于很多个参数传递的方式
- apply会自遍历数组/伪数组,然后按照元素顺序逐一传参。
function fn(a,b){
console.log(this);
console.log(a+b);
}
fn(10,20)
// 上下文调用:函数.call(修改this,形参1,形参2)
fn.call({name:'小明'},20,30)
// 上下文调用:函数.apply(修改this,数组/伪数组)
fn.apply({name:'小红'},[100,120,130])
二 apply的应用场景一伪数组转真数组
伪数组
什么是伪数组,自己创造一个伪数组。
概念:有数组的三要素(元素,长度,下标),但是没有数组的方法
为什么伪数组用不了数组的方法,是因为伪数组的原型对象并不是Array,所以伪数组的本质不是数组而是对象
伪数组转真数组
在ES5中用apply来进行伪数组转真数组
这里重点不是利用apply来修改this指向,而是利用他的数组传参和自动便利的特点
// 伪数组
let fakeArr={
0:20,
1:30,
2:40,
3:50,
4:60,
length:4
}
console.log(fakeArr);
// ES5
let arr1=[]
//这里不是利用apply修改this指向,而是利用他的数组传参和自动便利的特点
arr1.push.apply(arr1,fakeArr)
console.log(arr1,"利用apply来伪数组转真数组");
在ES6中用Array.from(伪数组),一键转换。
// 伪数组
let fakeArr={
0:20,
1:30,
2:40,
3:50,
4:60,
length:4
}
console.log(fakeArr);
// ES6 一键转换
let arr2=Array.from(fakeArr)
console.log(arr2,"222","利用Array.from伪数组转真数组");
二 apply的应用场景二求最大值和最小值
这里并不是利用apply来修改this指向,而利用apply的数组传参,自动遍历数组的特点
let arr=[1,4,5,6,6,3,9]
// 求最大值
let maxArr=Math.max.apply(Math,arr)
// 求最小值
let minArr=Math.min.apply(Math,arr)
console.log(maxArr);
console.log(minArr);
//apply的语法学起来很困难,现在es6通过展开运算符可以解决了
let arr1= Math.max(...arr)
console.log(arr1);
方法3 bind()
一 语法格式:函数.bind(修改this,参数1,参数2)
概念:bing不会立即调用函数,而是得到一个修改this之后的新函数
二 bind的应用场景一般用于修改不会立即执行的函数中this
常用于修改定时器的this,默认情况下定时器的this指向window
<script>
function fn(a,b){
console.log(this);
console.log(a+b);
}
// bing不会立即调用函数,而是得到一个修改this之后的新函数
// bind一般用于修改不会立即执行的函数中this:如定时器,事件处理函数
let fn1=fn.bind({name:'小黑'},2,4)
fn1(10,6)
setTimeout(function(){
console.log(this);
}.bind({name:"小黑"},10,30),3000)
</script>
三 总结三种方法的区别
1、传参的方式不同:
-
call是逐一传参,apply是数组/伪数组传参,传参的方式不同 2、执行的机制不同
-
call和apply是立即执行函数,bind不会立即执行函数
-
call和apply用一次,改一次
-
bind修改一次终生有效 3、注意点
-
修改this必须是引用类型,如果是string、number、boolean,则js编译器会自动转成对应的对象,如果是undefined和null,则修改无效。
四、闭包
一 、闭包的概念:
-
:一个函数对其周围状态的引用捆绑在一起或者说函数被引用包围,这样的组合就是闭包,也就是说闭包让你可以在一个
内层函数中访问到其外层函数的作用域,在JavaScript中每创建一个函数,闭包就会在函数创建的同时被创建出来。 -
总结起来就是:闭包是使用其他函数内部变量的函数(闭包=函数+其他函数内部变量) 二、闭包的作用
-
闭包是用来解决全局变量污染的问题
三、写一个简易的闭包
<script>
// 闭包:闭包是一个使用了其他函数内部变量的函数(函数+其他函数内部变量)
// 判断闭包
// 闭包作用
function fn(){
let num=1
function test(){
console.log(num);
}
test()
}
fn()
</script>
判断闭包1:从代码的角度来说满足两个条件:函数+使用其他函数局部变量
判断闭包2:从浏览器控制台,通过断点调试,出现Closure就是闭包
五、递归函数——深拷贝和浅拷贝
一 、什么是递归
概念:函数内部自己调用自己,他类似于我们的循环
他的应用场景:一般主要用递归函数来实现深拷贝和浅拷贝,第二个主要用递归函数实现dom树的遍历
二 深拷贝和浅拷贝
2.1 什么是浅拷贝:
浅拷贝:拷贝的是地址,修改拷贝后的数据对原数据有影响。
//浅拷贝
<script>
let obj={
name:'张三',
age:40,
sex:'男',
hobby:['学习','上课','跑步'],
student:{
name:'小张三',
age:20,
hobby:['听歌','吃饭','睡觉']
}
}
// 浅拷贝
let obj1=obj
obj1.hobby[0]='打豆豆'
obj1.name="张三三三三"
console.log(obj,"原本的数据");
2.2 什么是深拷贝
深拷贝拷贝的是数据,修改拷贝后的数据对原数据没有影响。
实现深拷贝的方式
json方式(推荐)
<script>
let obj={
name:'张三',
age:40,
sex:'男',
hobby:['学习','上课','跑步'],
student:{
name:'小张三',
age:20,
hobby:['听歌','吃饭','睡觉']
}
}
// 深拷贝
// (1)先把对象浅拷贝
// let jsonStr=JSON.stringify(obj)
//(2)在把json字符串转成js对象:JSON.parse(json字符串)
// let obj1=JSON.parse(jsonStr)
// 简写
let obj1=JSON.parse(JSON.stringify(obj))
obj1.hobby[0]='打架'
console.log(obj,"原本的数据");
console.log(obj1,"我是拷贝后的数据");
</script>
递归方式实现深拷贝
<script>
let obj={
name:'张三',
age:40,
sex:'男',
hobby:['学习','上课','跑步'],
student:{
name:'小张三',
age:20,
hobby:['听歌','吃饭','睡觉']
}
}
// newObj:拷贝后的新对象, obj:要拷贝的数据
function kaobei(newObj,obj){
for(let key in obj){
//如果是值类型,则直接拷贝数据,如果是key是引用类型,继续遍历引用
if(obj[key] instanceof Array){
newObj[key]=[]
kaobei(newObj[key] ,obj[key])
//如果是值类型,则直接拷贝数据,如果是key是引用类型,继续遍历引用
}else if(obj[key] instanceof Object){
// 对象
newObj[key]={}
kaobei(newObj[key],obj[key])
}else{
// 值类型
newObj[key]=obj[key]
}
}
}
let obj1={}
// 调用数组
// obj1是对象, obj是对象
kaobei(obj1,obj)
obj1.hobby[0]='打架'
console.log(obj,obj1);
</script>