JavaScript引用类型(三)Function函数类型总结
函数实际上是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。
由于函数是对象,因此函数名实际也是一个指向函数对象指针,不会与某个函数绑定。
一、函数的创建方法:
函数通常使用函数声明语法来创建函数,如下:
function sum(num1,num2){ // 函数声明 return num1 + num2; }
也可以使用函数表达是创建函数,如下:
var sum = function(num1,num2){ // 函数表达式 return num1 + num2 }
还有一种定义函数的方式是使用Function构造函数。Functiongou构造函数可以接受任意数量的参数,但是最后一个参数始终都被看成是函数体,而前面的参数则枚举出了新函数的参数,如下:
var sum = new Function ("num1", "num2", "return num1 + num2"); // 不推荐这样创建函数, **函数是对象,函数名是指针**
函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同,一个函数可能有多个名字,如下示例:
function sum(num1,num2){ // 声明创建函数 return num1 + num2; } console.log(sum(1,2)); var anot = sum; // 将 sum函数赋值给anot,此时anot和sum指向同一个函数 console.log(anot(10,20)); sum = null; // 将sum函数赋值成空 console.log(anot(40,90)); // 任然可以正常调用anot函数
二、函数没有重载(不能有同名函数在一起使用)
将函数名想像为指针,可以理解为什么js中函数没有重载的概念。
两个同名函数,后面函数会把前面的函数覆盖;
例如:
function fn1(num){ return num + 10; }; function fn1(s){ return s + 60; }; console.log(fn1(100))
三、函数声明和函数表达式
函数声明和函数表达式几乎是一样,但是本质上还是有区别的:
js解析器在执行环境中加载数据时,对函数声明和函数表达试并非是一视同仁。
以下是函数声明和函数表达式的区别:
解析器会率先读取函数声明,并使其在执行任何代码之前可以调用;
函数表达式必须等解析器执行到它所在的代码,在会真正被解释执行。
函数声明会将函数声明提升到顶部,函数表达式则不会。
直接上码:
// 函数声明代码: console.log(sum(10, 10)); // 20 function sum(num1,num2){ // 函数声明,可以函数的前后都可以调用 return num1 + num2; }; 上面代码开始执行之前,解析器就会做函数声明提升的过程,读取并将函数声明添加到执行环境中。 对代码求值时,js引擎在第一遍会声明函数并将它们放到源代码树的顶部。 所以,即使声明函数的代码在调用它的代码后面,js引擎也能把函数声明提升到顶部。 下面的代码之所以会报错,原因在于函数位于一个初始化语句中,而不是一个函数声明。 在函数所在的语句之前,变量sum中不会保存有对函数的引用,而且由于第一行代码导致的错误,下面的代码也不会执行。 //函数表达式代码: console.log(10,10); // Uncaught TypeError: sum is not a function var sum = function(num1,num2){ // 函数表达式,此时如果在此函数前面调用会报错,在后面调用,正常执行 return num1 + num2 }; console.log(10,10); // 20
除了什么时候可以通过变量访问函数这一点区别之外,函数声明与函数表达式的语法其实是等价的。
四、作为值的函数
在js中函数名本身就是变量,所以函数也可以作为值来使用,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回,如下代码:
function call(someFun,someArg){ // 第一个参数是个函数,第二个参数是传递给第一个参数函数的一个值 return someFun(someArg) } function add10(num) { return num +10; } var result = call(add10,10); console.log(result) // 20
五、函数内部属性
在函数内部有两个特殊的对象:arguments和this对象。
1、arguments
arguments是个类数组对象,包含着传入函数中所有的参数,虽然arguments的主要用途是保存函数参数(实参),但这个对象还有一个名叫callee的属性,该属性时一个指针,指向拥有这个arguments对象的函数。
callee是arguments对象的一个属性返回一个正在被执行函数的引用,例如:
function a(x,y){ console.log(arguments.length); // 2 console.log(arguments.callee.length); // 2 console.log(argurmnts.callee); // 返回函数体 }
ECMAScript 5 也规范化了另一个函数对象的属性: caller 。除了 Opera 的早期版本不支持,其他浏览器都支持这个 ECMAScript 3 并没有定义的属性。这个属性中保存着调用当前函数的函数的引用,
如果是在全局作用域中调用当前函数,它的值为 null 。
2、this
函数内部另一个特殊对象是this,this引用的是函数执行的环境对象(当网页的全局作用域中调用函数时,this对象引用的就是window),说白了this就是指向函数的调用,谁调用函数this就指向谁,例如:
window.color = "red"; var o = {color:"blue"}; function sayColor(){ console.log(this.color); // red 这个this指的是window } sayColor(); o.sayColor = sayColor; // 把函数赋值给对象 o 此时this就指向是对象o o.sayColor(); // blue
六、函数属性和方法
js中函数也是对象,是对象就有属性和方法。
每个函数都是两个属性:length和prototype。
length:表示函数参数的个数
function sayHi(){ alert("hi") } console.log(sayHi.length); // 0 函数的形参数是0个 function sayName(name){ alert(name) } console.log(sayName.length); // 1 函数的形参数是1个 function sum(num1,num2){ return num1 + num2; } console.log(sum.length); // 2 函数的形参数是2个
prototype:是保存所有引用类型实例方法的真正所在,例如toString()和valueOf()等方法实际上是保存在prototype名下,只不过是通过各自实例对象等访问。
每个函数都包含两个非继承而来的方法:apply()和call()。 4000 这两个方法都是在特定的作用域中调用函数,实际上是等于改变函数的this对象的指向的(改变函数的指向)。
apply()方法接收两个参数:
一个是运行函数的作用域,另一个参数是数组。其中第二个参数可以是数组(Array)实例,也可是是arguments对象,例如:
function sum(num1,num2){ return num1 + num2 } function callSum1(num1,num2){ return sum.apply(this,arguments); // // 第二个参数是个数组或者arguments } function callSum2(num1,num2){ return sum.apply(this,[num1,num2]); // 第二个参数是个数组或者arguments } console.log(callSum1(10,10)); // 20 console.log(callSum2(5,5)); // 10
在严格模式下,未指定环境对象而调用函数,则 this 值不会转型为 window。 除非明确把函数添加到某个对象或者调用 apply()或 call(),否则 this 值将是 undefined。
call()方法也是接收两个参数。
call()用法基本上和apply()方法一样,也是接收两个两个参数,第一个参数是运行在函数的作用域,和apply()方法的第一个参数一样,第二个参数必须逐个的列举出来。
function sum(num1,num2){ return num1 + num2; } function callSum3(num1,num2){ return sum.call(this,num1,num2); // call方法第二个参数必须明确的传入每一个参数,结果和apply一样 } console.log(callSum3(30,30)); // 60
call()方法参数有4种情况:
(1)、不传值,或者传入null,undefined,函数中的this指向的都是window
(2)、传递另一个函数的名字,函数中的this指向这个函数的引用
(3)、传递基本类型的时候,函数this指向其对应的包装对象,如String,Number,Boolea
(4)、传递对象,函数中this指向这个对象
example: function say1(){ console.log(this); // 输出say1函数的this对象 } function say2(){}; var sya3 = {name:"blue"}; say1.call(); // window say1.call(null); // window say1.call(undefined); // window say1.call("str"); // String {"str"} say1.call(1); // Number {1} say1.call(true); // Boolean {true} say1.call(say2); // say2函数 ƒ say2(){} say1.call(say3); // say3对象 sya3:{name:"blue"}
总结apply()和call()方法的区别
apply()和call()两个方法用法基本相同;
第一个参数都是一样的,指向运行在函数的作用域;
唯一不同的就是第二个参数:
apply()方法的第二个参数可以是arguments对象,也可以是个数组实例。
call()方法的第二个参数必须要明确的传入每一个参数,相当于是把数组的项拆开传入,结果都是一样的。很明显通过对比两个方法apply()方法相比call()简洁的多。
至于是使用 apply()还是 call(),完全取决于你采取哪种给函数传递参数的方式最方便。 如果你打算直接传入 arguments 对象,或者包含函数中先接收到的也是一个数组,那么使用 apply() 肯定更方便;否则,选择 call()可能更合适。(在不给函数传递参数的情况下,使用哪个方法都无所谓。)
实际上传参数不是真正使用apply()和call()方法的用武之地;这两个方法真正强大的地方是能扩充函数赖以运行的作用域:
window.color = "red"; var o = { color:"blue" } function sayColor(){ console.log(this.color) } sayColor(); // red 执行环境是window sayColor.call(window); // red 执行环境是window sayColor.call(this); // red 执行环境是window sayColor.call(o); // blue 执行环境变成 对象o
在看一个例子:
// 有一个对象banana= {color : "yellow"} ,我们不想对它重新定义 say 方法, 那么我们可以通过 call 或 apply 用 apple 的 say 方法: var banner = { color:"yellow" } function fruits(){} fruits.prototype = { color:"red", say:function(){ console.log("My color is"+' ' + this.color) } } // console.log(typeof fruits); // 函数 var apple = new fruits; apple.say(); // red apple.say.call(banner); // yellow 所以,可以看出 call 和 apply 是为了动态改变 this 而出现的, 当一个 object 没有某个方法(本例子中banana没有say方法), 但是其他的有(本例子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作。 // console.log(typeof apple); // object
js中还提供了一个bind()方法,这个方法会创建一个函数的实例(必须要创建成一个新函数,否则不能使用),其this值会被绑定到传给bind()函数的值。 bind() IE6,IE7,IE8不 支持
window.color = "red"; var o = { color:"blue" } function sayColor(){ console.log(this.color) } sayColor(); // red var objSayColor = sayColor.bin(o); // blue //sayColor()调用 bind()并传入对象 o,创建了 objectSayColor()函数。 object- SayColor()函数的 this 值等于 o,因此即使是在全局作用域中调用这个函数, 也会看到"blue"。
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
以上就是函数类型的总结。如有错误欢迎留言讨论^^
以下是一些call和apply两个方法使用小技巧:
// 1、数组之间的合并和追加 var arr1 = [1,2,"abc","blue",23]; var arr2 = ["a","b","c","d"]; Array.prototype.push.apply(arr1,arr2); console.log(arr1); //[1, 2, "abc", "blue", 23, "a", "b", "c", "d"] // 2、数组中最大值和最小值 var arr3= [2,12,34,5,67,6,9], var maxNumber1 = Math.max.apply(Math,arr3), maxNumber2 = Math.max.call(Math,12,3,4,32,66,4,2); console.log(maxNumber1,maxNumber2); // 67,66 number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。 //3、验证是否是数组(toString()方法没有被重写过) var arr = [1,2,3,4]; function isArray(obj){ return Object.prototype.toString.call(obj) === "[object Array]" } console.log(isArray(arr)); // true //4、 类(伪)数组使用数组的方法 Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象, 还有像调用 getElementsByTagName , document.childNodes 之类的, 它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。 但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象, 这样 domNodes 就可以应用 Array 下的所有方法了。 var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
apply、call、bind比较
那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。简单的一个示例:
var obj = { x: 10 }; var foo = { getX: function(){ return this.x; } } console.log(foo.getX.bind(obj)()); console.log(foo.getX.apply(obj)); console.log(foo.getX.call(obj)) 三个输出的都是10,但是注意看使用 bind() 方法的,他后面多了对括号。 也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候, 使用 bind() 方法。而 apply/call 则会立即执行函数。
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
- JavaScript高级程序设计学习总结三(JavaScript引用类型一)
- 详解JavaScript基础知识(JSON、Function对象、原型、引用类型)
- JavaScript学习笔记——引用类型:Object类型、function类型
- JavaScript--引用数据类型--function
- javascript学习笔记 - 引用类型 Function
- 浅谈JavaScript引用类型——Function
- 一步步学习javascript基础篇(3):Object、Function等引用类型
- Javascript引用类型之Function类型
- JavaScript的小小总结!变量类型、对象声明、this用法、原型、匿名函数、闭包和自执行函数
- javascript基础总结(三)--Function类型
- JavaScript中的函数的参数(简单类型,引用类型)+图解
- JavaScript基础篇(3)之Object、Function等引用类型
- JavaScript高级程序设计学习总结四(JavaScript引用类型二)
- JavaScript基础篇(3)之Object、Function等引用类型
- ASP.NET AJAX 说明文档->客户端引用->全局命名空间->JavaScript 基础类型扩展->Array 类型扩展->add 函数
- JavaScript引用类型-函数
- JavaScript引用类型——Function
- JavaScript笔记——引用类型之Object类型和Function类型
- JavaScript中的Function类型总结
- ASP.NET AJAX 说明文档->客户端引用->全局命名空间->JavaScript 基础类型扩展->Array 类型扩展->addRange 函数