一、前言
ECMAScript中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length和prototype。每个函数也包含两个非继承来的方法:apply()和call(),还有一些继承而来的方法,比如valueOf()等。
二、函数属性:length、prototype
2.1、length
函数希望接收的命名参数的个数,如:
function sum(a,b){
return a+b;
}
sum.length ; //2 function add(){ }
add.length; //0
2.2、prototype
它是保存所有实例方法的真正所在。比如toString()和valueOf()等方法都是保存在prototype名下,只不过是通过各自的对象的实例访问而已。在创建自定义引用类型和以及实现继承的时候,这个属性尤为重要。且这个属性是不可枚举的所以for in 访问不到,这个暂且不讲这个属性。
三、函数的方法: apply() 和call()
每个函数都包含这两个非继承而来的方法,都是Function.prototype的方法。用途都是在特定的作用域中调用函数。
都是用来改变函数的运行时的上下文对象(执行上下文),或者说是改变函数内部的this指向。
3.1、两者区别:
apply()方法接收两个参数,一个是在其中运行函数的作用域,一个是参数数组,(这个参数数组可以是Array的实例,也可以是类数组arguments;)
call()方法和apply()的作用相同,区别仅在于接收的参数的方式不一样,使用call时候参数必须逐个列举出来传给函数。
function sum(num1, num2){
return num1 + num2;
} function callsum1(num1, num2){
return sum.apply(this,arguments); //return sum.apply(this, [sum1,sum2]);
} function callsum2(num1, num2){
return sum.call(this,num1, num2);
}
当参数明确的时候,建议使用call,但传入的参数不明确的时候,或者是数组的时候,建议使用apply。
3.2、apply()和call()真正强大的地方:
扩充函数赖以运行的作用域,这就使得对象不需要和方法有任何耦合的关系。看如下demo.
var animal = {
words:'......',
speak:function(say){
console.log(say + ' ' + this.words);
}
} var dog ={
words: 'wang'
}
//dog没有say这个方法,但是可以通过call来调用aniaml的say方法。 animal.speak.call(dog, 'speak'); //dog就是call()这个方法的上下文,this指向了dog这个对象。
3.3、原生实现
Function.prototype.myCall = function (content) {
console.log(typeof this); context = context || window;
context.fn = this;
const args = [...arguments].silce(1);
const result = context.fn(...args);
delete context.fn; return result;
}
3.4、说明:
非严格条件下,call和apply的第一个参数为null、undefined、空 ,函数内的this指向windown(浏览器 ) 或global。
严格模式下,null是null,undefined和空 为undefined
四、bind()
ECMAScript 5 中定义了这个方法,这个方法会创建一个函数的实例,其中this值会被绑定到传给bind()函数的值。
因为在IE9以下不支持原生的bind函数,但是对apply是支持的,所以可以利用apply自定义bind函数,如下:
Function.prototype.myBind = function () {
const _this = this; //保存调用bind的原函数
const context = [].shift.call(arguments);//保存需要绑定的this上下文,就是第一个参数
const args = [].slice.call(arguments);//剩余参数转为数组 return function () {
//我们将 bind 的其余参数和调用bind后返回的函数在执行的过程中接收的参数进行拼接,
//作为一个数组传入apply的第二个参数中去。
_this.apply(context, [].concat.call(args, [].slice.call(arguments)]);
}
}
在bind()中创建了一个闭包,闭包中使用apply()调用传入的函数,并给apply传递context和arguments参数(arguments对象是内部函数的,不是bind函数的),
当调用返回函数的时候,它会在给定的环境中执行被传入的函数,并给出所有的参数。这也是函数绑定的常用方法。
看看vue源码中是怎么自定义的bind的,感慨~~~ 一个优秀的框架真的要考虑很多啊:
五、bind vs call、apply
bind返回的是对应的函数,不会立即调用,而call、apply都是立即调用的。
下面的只是一个简单的例子,用来说明bind的用处,特别注意的是,定时器中的this如果不在执行函数中bind(this),则里面的this就会指向window,而不是当前创建的对象中。可以这么理解,setTimeout是window的,那么在执行函数的this也会指向Windows,除非bind(this)前置把里面函数的执行上下文改成当前对象。
var slider={
LWIDTH:0,
DURATION: 1000,
WAIT:3000, //自动轮播之间的等待时间
timer:null, //保存一次性定时器序号
canAuto:true,
LHeight:60,
init:function(){
this.updateView();
$(".slider").hover(
function(){this.canAuto=false;}.bind(this),
function(){this.canAuto=true;}.bind(this)
)
this.autoMove();//判断之后,调用自动执行函数
},
autoMove:function(){
this.timer=setTimeout(
function(){
if(this.canAuto){
this.move(1);
}else{
this.autoMove();
}
}.bind(this),
);
}
}
六、应用场景
是不是经常遇到这样的用法?
Array.prototype.slice.call ( arguments);
或者是:
[].prototype.slice.apply(arguments);
或者是:
function isArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]' ;
或者是:
function(){XXXXXXx}.bind (this);
jQuey中不也有bind方法么?
$('.XXX').bind('click',function(){});
6.1、利用apply传参数机制,在某些本来需要写成遍历数组变量的任务中使用内建的函数
定义一个 log 方法,让它可以代理 console.log 方法
function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2
6.2、Math.max/Math.min来找出一个数组中的最大/最小值。
var numbers = [2, 8, 4, 3, 1];
var max = Math.max.apply(null, numbers); //等价于:Math,max(2, 8, 4, 3, 1);
var min = Math.min.apply(null, numbers); //同上 //es6中可以用数组的扩展运算符替代apply
Math.max(...numbers)
6.3、方便实现继承
function Animal(words){
this.words = words;
this.speak = function(){
console.log(this.words);
}
} function Dog(words){
Anaimal.call(this, words);//或者Anmal.apply(this,, arguments) } var dog = new Dog('wang');//这样实例的dog对象就有了Animal的speak方法了。 dog.speak();
函数的属性和方法之call、apply 及bind的更多相关文章
-
js函数的属性和方法
js函数的属性和方法 前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解j ...
-
Python使用property函数定义属性访问方法如果不定义fget会怎么样?
我们知道Python使用property函数定义属性访问方法时的语法如下: 实例属性=property(fget=None, fset=None, fdel=None, doc=None) 而是要@p ...
-
javascript中Math函数的属性与方法
math函数的属性 Math.PI:返回圆周率. math函数的方法 绝对值: Math.abs(); 对数进行上舍入: Math.ceil(); 对数进行下舍入: Math.floor(); Mat ...
-
函数的属性和方法, apply和call的区别及bind的使用
==>我的新博客中 http://www.suanliutudousi.com/2017/08/27/%E5%87%BD%E6%95%B0%E7%9A%84%E5%B1%9E%E6%80%A7% ...
-
JS学习之函数内部属性和方法
知识点:arguments和this对象.caller属性.apply()和call()方法 arguments对象:函数内部对象,传入函数中所有参数的集合,类数组对象 属性:callee 指 ...
-
函数也是对象,本片介绍函数的属性、方法、Function()狗仔函数。
1.arguments.length表示实参的个数. 2.arguments.callee.length表示形参个数. function test(a,b,c,d,e,f){ alert(argume ...
- JS学习之函数的属性和方法
-
深入理解javascript函数系列第三篇——属性和方法
× 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...
-
内置对象(Math对象、Date对象、Array对象、String对象)常用属性和方法
Math对象 Math 是一个内置对象, 它具有数学常数和函数的属性和方法.不是一个函数对象. 与其它全局对象不同的是, Math 不是一个构造函数. Math 的所有属性和方法都是静态的. 跟数学 ...
随机推荐
-
使用backbone的history管理SPA应用的url
本文介绍如何使用backbone的history模块实现SPA应用里面的URL管理.SPA应用的核心在于使用无刷新的方式更改url,从而引发页面内容的改变.从实现上来看,url的管理和页面内容的管理是 ...
-
mvc+webapi 单元测试
1.前言 现在这个项目已经有阶段性的模块完成了,所以就想着对这些模块进行单元测试,以保证项目的代码的质量.首先虽然标题是mvc+webapi实质上我只是对mvc进行的测试.用的时候vs的unit te ...
-
1.8 基础知识——GP2.7 识别和卷入干系人(Stakeholder) &; GP2.9 质量保证(QA)
GP2.7 识别和卷入干系人(Stakeholder) GP2.7 Identify and involve the relevant stakeholders of XXX process as p ...
-
ORA-12705: Cannot access NLS data files or invalid environment specified
ASM实例无法启动 [grid@data ~]$ sqlplus / as sysasm SQL*Plus: Release 11.2.0.4.0 Production on Fri Sep 11 0 ...
-
【转】堆栈跟踪中收到一个UnhandledExceptionFilter调用时,如何查找问题异常堆栈
定义没有异常处理程序来处理引发的异常时调用UnhandledExceptionFilter函数.函数通常将异常传递到捕获并处理它所尝试的 Ntdll.dll 文件. 在某些情况下,在其中存在的进程内存 ...
-
用TLS实现安全TCP传输及配置和访问https的web服务(转)
tls相关 大致原理 为了让两个之间实现安全传输,(我们把服务端统一叫做TcpServer,客户端统一叫做TcpClient),TcpServer在listen完了accept之后要用一个证书来声明自 ...
-
python_鸡兔同笼问题
鸡兔同笼问题 -- 今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何? --鸡和兔在一个笼子里,从上面数,有35个头:从下面数,有94只脚.问笼中各有几只鸡和兔 如何逻辑整理? -- 鸡头和兔子 ...
-
左偏树(BZOJ4003)
左偏树打个标记,没了. #include <cstdio> #include <vector> using namespace std; typedef long long l ...
-
MariaDB第二章:基本增删改查
MariaDB 数据类型 MariaDB数据类型可以分为数字,日期和时间以及字符串值. 使用数据类型的原则:够用就行, 尽量使用范围小的,而不用大的 常用的数据类型 整数:int, bit 小数:de ...
-
什么是CLOS架构?
Clos架构,诞生于1952年,是由一位叫Charles Clos的人提出的,所以它并不是一个新的概念. 这个架构主要描述了一种多级电路交换网络的结构.Clos最大的优点就是对Crossbar结构的改 ...