函数进阶 call apply bind 的区别
文章目录
- 一、函数的定义方式
- 二、六大触发函数的方式
- 2.1 普通函数
- 2.2 对象的方法
- 2.3 构造函数
- 2.4 绑定事件的函数
- 2.5 定时器函数
- 2.6 立即执行函数
- 三、函数内 this 的指向
- 3.1 改变函数内部 this 的指向
- 3.2 call() 方法
- 3.3 apply() 方法
- 3.4 bind() 方法 不需要立即调用的同时改变 this
- 3.5 call apply bind 总结
- 四、递归
- 4.1 递归求阶乘
- 4.2 斐波那契数列
- 4.3 递归遍历数据
- 五、浅拷贝 和 深拷贝
- 5.1 浅拷贝
- 5.1.1 for 循环实现
- 5.1.2 Object.assign实现:
- 5.2 深拷贝
- 六、立即执行函数
一、函数的定义方式
// 1. 自定义函数(命名函数)
function fn(){}// 2.函数表达式 (匿名函数)
let fun = function (){}// 3. 利用 new Function("参数1","参数2","参数3")
// let f = new Function("console.log(123)")
// f() // 123let f = new Function("a", "b", "console.log(a+b)")
f(1, 2) // 3
注意:
- Function 里边的参数都必须是字符串格式
- 第三种方式执行效率低,也不方便书写,因此较少使用
- 所有函数都是 Funtion的实例(对象)
console.log(f instanceof Object); // true
函数的内容原理:
二、六大触发函数的方式
2.1 普通函数
调用方式:fn() or fn.call()
this 指向 window
function fn() {console.log("人生的巅峰")
}
fn()
fn.call()
2.2 对象的方法
调用方式:obj.sayHi()
this 指向当当前对象 obj
let obj = {sayHi: function () {console.log("人生的巅峰")}
}
obj.sayHi()
2.3 构造函数
调用方式:new 构造函数的时候调用
this指向 实例对象
function Star(){}new Star()
2.4 绑定事件的函数
调用方式:事件触发时调用
this 指向 事件对象
btn.onclick = function (){ //点击按钮即可调用函数console.log("人生的巅峰")
}
2.5 定时器函数
调用方式:定时器时间到达时调用
this 指向 window 因为完整写法是 window.setInterval
setInterval(() => {console.log('...');
}, 1000);
2.6 立即执行函数
调用方式:立即调用
this 指向 window
(function (){console.log("立即执行函数");
})()
三、函数内 this 的指向
这些this的指向,是当我们调用函数的时候确定的。调用方式的不同決定了this的指向不同
一般指向我们的调用者。
调用方式 | this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象,原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
3.1 改变函数内部 this 的指向
Javascript为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部this的指向问题,常用的有 bind()、call()、appy() 三种方法。
3.2 call() 方法
call(obj,arg1,arg2,ar3,...)
call() 方法的主要作用有两个:
- 一是可以调用该函数,类似于 你有一个函数
function demo(){...}
, 你 平时或许是demo()
这样调用它的,你也可以demo.call()
,它们的本质上没有什么区别 - 二是你可以改变函数内
this
的指向,具体请看下面示例:
let obj = {name: "andy"
}function fn(a, b) {console.log(this)console.log(a + b);
}
fn(2, 3) // window 5
fn.call(obj, 2, 3) //{name: "andy"} 5
call() 方法在实际开发中主要用于继承使用。
call() 实现继承实例:
// 手写继承
function Father(name,age){this.uname = namethis.uage = age
}
Father.prototype.sing = function (){console.log("我会唱歌")
}function Son(name,age){Father.call(this,name,age)
}Son.prototype = new Father()
Son.prototype.dance = function (){console.log("我会跳舞");
}
Son.prototype.constructor = Sonlet objFather = new Father("父亲",30)
let objSon = new Son("儿子",18)console.log(objFather);
console.log(objSon);
3.3 apply() 方法
apply(obj,[array])
apply() 方法与 call() 方法很相似,主要区别在于 参数
apply: 的第二个参数 是接受一个数组作为参数,而 call() 方法是一串参数列表
apply:最多只能有两个参数——新this对象和一个数组argArray。如果给该方法传递多个参数,则把参数都写进这个数组里面,当然,即使只有一个参数,也要写进数组里。如果argArray不是一个有效的数组或arguments对象,那么将导致一个TypeError。
call:它可以接受多个参数,第一个参数与apply一样,后面则是一串参数列表。这个方法主要用在js对象各方法相互调用的时候,使当前this实例指针保持一致,或者在特殊情况下需要改变this指针。
apply 的主要应用示例:
let arr = [1,66,3,99,4]
let maxNum = Math.max.apply(null,arr)
let minNum = Math.min.apply(null,arr)console.log('maxNum: ', maxNum); // 99
console.log('minNum: ', minNum); // 1
3.4 bind() 方法 不需要立即调用的同时改变 this
bind方法 不会调用函数 。但是能改变函数内部this指向
fun.bind(thisarg, argl, arg2, ...)
- thisarg:在fun函数运行时指定的this值
- arg1,arg2:传递的其他参数
- 返回的是原函数 修改this 之后产生的新函数
示例:
let obj = {name:"andy"
}function fn(){console.log(this)
}
let newFn = fn.bind(obj) // 不会调用,没有结果输出
newFn() // {name: "andy"}
应用场景:
如果有的函数我们不需要立即调用但是又想改变这个函数内部的this指向此时用bind()方法
bind() 方法应用:
let btn = document.querySelector("#btn")// bind() 应用案例
btn.onclick = function () {this.disabled = truelet that = thissetTimeout(function () {// 直接写不起作用,因为 this 指定的是 window ,绑定 bing(this)之后才起作用this.disabled = false // 方案二:// that.disabled = false }.bind(this), 3000); // 因为 this是在定时器外边而又在 btn的事件处理程序里边,所以 this 指向的是 btn// }.bind(btn), 3000); //效果相同,但是耦合过高
}
此外还有一种奇怪的用法:
这里实现的需求是, 在不改变函数内部this的同时,传入一个 this,
当然,你可以在全局定义一个 that,然后直接在函数内部使用,这里使用 bind 只是另外一种解决方案。
3.5 call apply bind 总结
相同点:
- 都可以改变 函数内部的 this
不同点:
- call() 和 apply() 会调用函数,而 bind() 不会调用函数
- call() 和 bind() 传入的参数都是一串参数列表,而 apply传递的参数必须是数组形式
主要应用场景:
- call() 主要处理继承
- apply() 主要处理 数组相关的
- bind() 不调用函数改变this指向。比如改变定时器内部的this指向
四、递归
4.1 递归求阶乘
一个数的阶乘:n!=1×2×3×…(n-1)×n
// 1.递归求阶乘
function fn(n){if(n == 1){return 1;}return n * fn(n-1)
}
console.log(fn(5)); // 120
4.2 斐波那契数列
0、1、1、2、3、5、8、13、21、34
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
function fb(n) {if (n === 1 || n === 2) {return 1}return fb(n - 1) + fb(n - 2)
}
console.log(fb(6));function f(n, a1 = 1, a2 = 1) {if (n <= 1) {return a1};return f(n - 1, a2, a1 + a2);
}
console.log(f(6)); // 可以计算到1450位,不溢出
4.3 递归遍历数据
<script>var data = [{id: 1,name: '家电',goods: [{id: 11,gname: '冰箱',goods: [{id: 111,gname: '海尔'}, {id: 112,gname: '美的'}, ]}, {id: 12,gname: '洗衣机'}]}, {id: 2,name: '服饰'}];// 我们想要做输入id号,就可以返回的数据对象// 1. 利用 forEach 去遍历里面的每一个对象function getID(json, id) {var o = {};json.forEach(function(item) {// console.log(item); // 2个数组元素if (item.id == id) {// console.log(item);o = item;// 2. 我们想要得里层的数据 11 12 可以利用递归函数// 里面应该有goods这个数组并且数组的长度不为 0 } else if (item.goods && item.goods.length > 0) {//这里需要接收 内部 return 的 oo = getID(item.goods, id);}});return o;}console.log(getID(data, 1));console.log(getID(data, 2));console.log(getID(data, 11));console.log(getID(data, 12));console.log(getID(data, 111));
</script>
五、浅拷贝 和 深拷贝
- 浅拷贝只是拷贝一层,更深层次 对象级别 的 只拷贝引用 。
- 深拷贝拷贝多层,每一级别的数据都会拷贝
5.1 浅拷贝
5.1.1 for 循环实现
let obj = {id: 1,name: "andy",msg: {age: 18}
}let newObj = {}for (key in obj) {newObj[key] = obj[key]
}
// newObj 修改 obj的也会改,因为这是浅拷贝对于对象级别的拷贝是,拷贝指针
newObj.msg.age = 50console.log(obj);
console.log(newObj);
5.1.2 Object.assign实现:
Object.assign
返回目标对象。
语法:
Object.assign(target, ...sources)
target 目标对象。
sources 源对象。
let obj = {id: 1,name: "andy",msg: {age: 18}
}
const copy = Object.assign({},obj)
copy.msg.age = 50console.log(obj);
console.log(copy);
5.2 深拷贝
注意:判断时要先把数组写在上面,因为 数组也属于对象,如果写在下边会错把数组当对象。
let obj = {id: 1,name: "andy",msg: {age: 18},color:['white','black']
}
let objTwo = {}
function deepCopy(newObj, oldObj) {for(var k in oldObj){// 判断我们的属性值属于哪种数据类型// 1.获取我们的属性值 oldObj[k]var item = oldObj[k]// 2.判断值是否是 数组,对象if(item instanceof Array){newObj[k] = []deepCopy(newObj[k],item)}else if(item instanceof Object){newObj[k] = {}deepCopy(newObj[k],item)}else{newObj[k] = item}}
}
deepCopy(objTwo,obj)
console.log(objTwo);
六、立即执行函数
// 写法一:
(function (a, b) {num = a + bconsole.log(num); // 5
})(3, 2);// 写法二:
(function (a, b) {console.log(a + b); // 30
}(10, 20));
函数进阶 call apply bind 的区别相关推荐
- 创建对象的方式以及call,apply,bind的区别
创建对象的方式有四种 1.直接量 var obj={ 属性名:属性 方法名:function(){} } 2.通过构造函数创建对象,这样能够批量创建多个具有相同属性的子对象(顺便介绍call,appl ...
- JS函数方法Call Apply Bind运用
JS 函数非继承的call和apply方法 同:call & apply 主要是用于扩展this指向,降低this作用域与函数之间的耦合度: 区别:传参差异 function.call(thi ...
- 前端学习(1115):call apply bind的区别
- call,apply,bind的区别
1 转载于:https://www.cnblogs.com/YangJonathan/p/11223641.html
- JS高级—call(),apply(),bind()
文章目录 call() 介绍 语法 特点 返回值 使用(主要应用) apply() 介绍 语法 特点 返回值 使用 bind() 介绍 语法 特点 返回值 使用 call(),apply(),bind ...
- javascript函数进阶详细内容 函数闭包 箭头函数 call bind apply用法 偏函数 回调函数
JS函数进阶 这次的内容我会给大家详细介绍函数方面的内容 1.箭头函数:ES6新增的定义函数的方式,箭头函数是用来简化函数定义语法的. - 箭头函数的语法: ()=>{} ()里面写形式参数,{ ...
- javascript中apply、call和bind的区别
在JS中,这三者都是用来改变函数的this对象的指向的,他们有什么样的区别呢. 在说区别之前还是先总结一下三者的相似之处: 1.都是用来改变函数的this对象的指向的. 2.第一个参数都是this要指 ...
- call(),apply()和bind()的区别和应用以及扩展
首先三个方法的作用: 1:都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域. 2:都是改变this的指向方向 区别: call()和apply()接受参数的方 ...
- call apply bind 的作用和区别
1.call apply bind 的作用和区别 作用: 都可以改变函数内部的this指向. 区别点: 1.call和apply会调用函数,并且改变函数内部this指向. 2.call和apply传递 ...
- call apply 和 bind的区别
apply 和call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. Ja ...
最新文章
- GitHub开源的超逼真俄罗斯方块游戏
- [Object-C语言随笔之二] 《NSLog》常用的打印调试语句与自动排版
- linux 从grub 进入rescue,在grub的rescue模式修复linux引导
- 小工匠聊架构-超高并发秒杀系统设计 01_总体原则和架构演进
- 【Python】raise ValueError(Too many dimensions: %d %d. % (ndim, ndmax))问题
- kafka Linux 下启动服务 测试,Linux下安装部署Kafka分布式集群与测试
- c语言 iostream,C语言 我应该在哪里使用iostream类?
- switch如何更新大气层,和进入hekate界面
- 安徽省计算机二级水平考试试卷,2010安徽省全国计算机等级考试二级笔试试卷VB试题及答案...
- go语言中的try、catch、throw实现
- 转发页面,并且传参数,@click@dblclick冲突问题
- jmonkeyEngineSDK安装部署及IDEA集成JME3开发
- JavaScript学习之旅-导言篇
- 服务器文件变更监控,监控文件夹变化并ftp到服务器
- ios10苹果手机页面定位不准问题
- 【数据处理】Python,matplotlib 如何画柱状图?如何画各种类型的柱状图?柱子宽度设置;设置X轴刻度用label显示;设置柱子距离x轴的高度;设置柱体颜色;设置柱体描边;并列、多条柱状图
- Java swing 单机版五子棋
- 无锡设计培训——室设行业现状与发展前景
- Linux固态硬盘 设置写入缓存,固态硬盘性能的背后:浅论写入缓存设置
- 基于Javaweb高校毕业生实习管理系统 .rar(论文+项目源码)