JS调用模式以及bind()方法
本来是想好好归纳下bind()方法,但是从中又牵扯出了现在读的js语言精粹的一些知识,那这里就从基础开始整理下知识点。
函数
JS中最重要的组成部分就是函数了,由于JS中没有类之说,类的功能实现也是靠函数来完成的,用函数模拟类继承等问题。
JS中的函数就是对象,对象是“名/值”对的集合并拥有一个连到原型对象的隐藏连接。每个函数对象在创建时也随配有一个prototype属性。它的值是一个拥有constructor属性且值即为该函数的对象。
因为函数是对象,所以它们可以像任何其他的值一样被使用。函数可以保存在变量,对象和数组中。函数可以被当做参数传递给其他函数,函数也可以再返回函数。而且,因为函数是对象,所以函数可以拥有方法。
函数字面量
函数对象通过函数字面量来创建:
//创建一个名为add的变量,并用来把两个数字相加的函数赋值给它。
var add = function (a, b) {
return a + b;
}
函数字面量包含四个部分,保留字function,函数名(可省略,即匿名函数),参数,函数体。
通过函数字面量创建的函数对象包含一个连到外部上下文的连接。这被称为闭包,它是JS强大表现力的来源。
闭包
在js中,每次函数调用时,新的作用域就会产生。
在某个作用域中定义的变量只能在该作用域或其内部作用域(改作用域中定义的作用域)中才能访问到。也就是说内部函数变量可以访问到外部,而外部不能访问到内部。
var a = 10;function out() {a == 10;//truevar a = 11;function inner() {a == 11;//true}inner(); };out();
然后这段代码是来自一本书的,今天才在图书馆借的,上面闭包解释也是这本书的意思,配着闭包的例子就是这个,但是这个例子代码却是错的,
这个错误其实就是JS里面变量提升的问题,详细可见下一篇博客,,这里的执行顺序其实是这样的:
也就是说变量的声明会提前,这里的a还没有赋值,当然是undefined和false,书上配着闭包的代码原本应该是这个意思:
var a = 10;function out() {console.log(a == 10);//truea = 11;function inner() {console.log(a == 11);//true}inner(); };out();
把函数里面的var去掉就OK了,没有二次声明,就不会出现这个问题了。好了好了扯远了,本来是说闭包的...
继续.........
自执行函数是一种机制,通过这种机制声明和调用一个匿名函数,能够达到仅定义一个新的作用域的作用。
var a = 3;(function () {var a = 5; })();console.log(a == 3);//true
自执行函数对声明私有变量是很有用的,这样可以让私有变量不被其他代码访问。
类和继承
上面也说到,JS没有class关键词。类只能通过函数来定义.......由于这里不想讲这里的,这里省略。
调用模式
方法调用模式
当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。如果调用表达式包含一个提取属性的动作(即包含一个.. 点表达式或[subscript]下标表达式),那么它就是被当做一个方法来调用。
一个小例子:
var myObject = {value : 0,increment: function (inc) {this.value += typeof inc === 'number' ? inc : 1;} };myObject.increment(); console.log(myObject.value); //1 myObject.increment(2); console.log(myObject.value); //3
方法可以使用this访问自己所属的对象,所以它能从对象中取值或对对象进行修改。this到对象的绑定发生在调用的时候。这个“超级”延迟绑定(very late binding)使得函数可以对this高度复用。通过this可取得它们所属对象的上下文的方法称为公共方法。
函数调用模式
当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用的:
var sum = add(3, 4); //sum的值为7。
以此模式调用函数时,this被绑定到全局对象。这是语言设计上的一个错误。倘若语言设计正确,那么当内部函数被调用时,this应该仍然绑定到外部函数的this变量。这个设计错误的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享改方法对对象的访问权。
解决方案:
1.把该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this。按照约定,把那个变量命名为that
var add = function (a, b) {return a + b; };var myObject = {value : 0,increment: function (inc) {this.value += typeof inc === 'number' ? inc : 1;} };myObject.increment(); console.log(myObject.value); //1 myObject.increment(2); console.log(myObject.value); //3myObject.double = function () {var that = this;var helper = function () {that.value = add(that.value,that.value);};helper(); //以函数的形式调用helper,这里形式被调用时this指向全局对象,浏览器中是window,我们这里提前把this存在that里面 };//以方法的形式调用double myObject.double(); console.log(myObject.value);
2.
用call,apply,bind方法解决,前面有博客讲到了call和apply的使用,这里用bind来解决。
bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
在上面例子中,我们也可以用bind这样写:
myObject.double = function () {// var that = this;var helper = function () {this.value = add(this.value,this.value);}.bind(this);//bind换绑thishelper(); };//以方法的形式调用double myObject.double(); console.log(myObject.value);
在常见的单体模式中,通常我们会使用 _this , that , self 等保存 this ,这样我们可以在改变了上下文之后继续引用到它。
我们用另一种形式再举一个例子:
var class = {bar : 1,eventBind: function(){var that = this;$('.someClass').on('click',function(event) {console.log(that.someValue); });} }
还是Javascript 特有的机制,上下文环境在 eventBind:function(){ } 过渡到 $('.someClass').on('click',function(event) { }) 发生了改变,这里还是用that保存了this来解决。用bind方法似乎更优雅的解决:
var class = {bar : 1,eventBind: function(){$('.someClass').on('click',function(event) {console.log(this.someValue); //1}.bind(this));} }
bind() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,这里我们传入想要的上下文 this(其实就是 class )到 bind() 函数中。然后,当回调函数被执行的时候, this 便指向class对象。
继续看一个例子:
var u1 = function(){console.log(this.x); } var f1 = {x:3 } u1(); // undefined var res = u1.bind(f1); res(); // 3
创建了一个新的函数 res,当使用 bind() 创建一个绑定函数之后,它被执行的时候,它的 this 会被设置成f1 , 而不是像我们调用 u1 时的全局作用域。
构造器调用模式,Apply调用模式、
这里不写了,详情可参考JS语言精粹书。
其他一些问题
bind的使用不能叠加,如果连续 bind() 两次,亦或者是连续 bind() 三次,最终绑定的还是第一次bind的对象,详情参考bind()实现原理, 深层次原因,bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。(???)
apply、call、bind比较
举一个最直接的例子:
var obj = {value: 1111, };var foo = {getValue: function() {return this.value;} };console.log(foo.getValue.bind(obj)()); //111 console.log(foo.getValue.call(obj)); //111 console.log(foo.getValue.apply(obj)); //111
唯独 bind() 方法后面多了对括号。
区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
最后的总结:
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
当然还有很多没说到,有很多都省略过去了,任何一个点都可以在JS里面扩展到很多,这里重在讲明白基本要点,任何一点可上网或者查相关书籍找资料继续深究。以上引用了很多网上和书籍资料。
转载于:https://www.cnblogs.com/zhangmingzhao/p/7711965.html
JS调用模式以及bind()方法相关推荐
- JSPatch实现原理详解:让JS调用/替换任意OC方法
JSPatch实现原理详解:让JS调用/替换任意OC方法 2015-07-10 09:05 编辑: suiling 分类:iOS开发 来源:bang JSPatch以小巧的体积做到了让JS调用/替换任 ...
- 关于火车采集js调用文章资讯的方法 淘宝采集 相关
关于火车采集js调用文章资讯的方法 心岛发布于2014年12月16日 分类: 数据采集 浏览:761 人次 评论:0 一.需要准备的工具软件: 火车头采集器 抓包软件(我使用的是fiddler) 二 ...
- [转]JS调用Android里面的方法,Android调用JS里面的方法
FROM : http://blog.csdn.net/hj563308597/article/details/45197709 Android WebView 在公司Android的开发过程中遇到一 ...
- JS调用CS里的方法:PageMethods
想要在页面里JS代码里onclick去调用后台文件中的一个方法,搞了半天,才弄懂怎么做. 原来是通过PageMethods来实现的. 举个列子: Default.aspx 里代码 <%@ Pag ...
- js原生方式实现bind方法
1.思路 (1)因为bind方法不会立即执行函数,需要返回一个待执行的函数(这里用到闭包,可以返回一个函数)return function(){} (2)作用域绑定,这里可以使用apply或者call ...
- JS创建对象模式7种方法详解
创建对象的几种模式 虽然Object构造函数或者字面量,都可以用来创建对象,但这些方式有明显的缺点:使用同一个接口创建很多对象,会产生大量的代码, 于是,工厂模式诞生了 1 工厂模式 工厂模式是广为人 ...
- js 调用C#.NET后台方法 转载自:http://www.cnblogs.com/lizhao/archive/2010/11/23/1990436.html...
第一种: <script type="text/javascript"> $(document).ready(function() { sshow(); }); ...
- js中自己实现bind方法及详解
1. 详细代码如下: if (!Function.prototype.bind) {Function.prototype.bind = function () {var self = this, // ...
- js调用android相册,【方法】移动端H5如何调用相册和相机上传图片、音频、视频...
在移动端上传图片方法很简单,使用HTML5中的input:file供文件上传. <一>常用属性值: 1.accept:规定文件上传来提交的文件类型,此属性只能和type:file配合使用 ...
最新文章
- execSQL()方法和rawQuery()方法
- opencv 直线检测笔记
- SVN状态图标不显示
- OpenGL normalviewer普通视图的实例
- 睡眠音频分割及识别问题(三)
- Spring DataSource JNDI - Tomcat JNDI 配置示例
- Flutter 基础系列篇
- 【BZOJ 3681】Arietta
- PKU《程序设计导引及在线实践》刷题记录(上)
- Hadoop权威指南:知识梳理(一)
- dBm与功率(w)换算关系!
- VR家庭火灾安全系统_VR校园火灾安全系统_VR工厂火灾安全系统_VR宿舍火灾安全系统多场景选择
- simpson公式matlab实现,数值分析复化梯形公式复化Simpson公式MATLAB程序
- asp服务器管理系统,ASP服务器软件
- 点亮LED-STM32电控学习笔记03
- 参考文献格式生成器(GB/T 7714-2015)
- linux下java加斜杠成了w符号_linux 特殊符号大全
- 05-Spring反转控制IOC 与 依赖注入DI概念
- 计算机网络的标准和单位
- 分享微信小程序开发详细步骤
热门文章
- 数字信号处理5:FIR滤波器设计
- 从714里连续减去6减几次得0_小学数学1—6年级基础知识整理 ,预习复习都能用...
- go 302不记录cookie_gin pprof 记录日常操作
- asp代码在dwearwear转换成html格式怎么转,为前端而生的编辑器Brackets及配置推荐
- 蓝桥杯嵌入式板-解决LCD使LED亮灭混乱的办法
- XML 解析错误:格式不佳
- 软件portable
- html5+php调用android手机图片,HTML5拍照上传图片Phonegap封装HTML5调用Android相机拍照上传到PHP端...
- (转载)js对象原来也有类、实例属性和原型属性
- MySQL远程访问权限,允许远程连接的开启