前面的話
函數是javascript中特殊的對象,可以擁有屬性和方法,就像普通的對象擁有屬性和方法一樣。甚至可以用Function()構造函數來創建新的函數對象。本文是深入理解javascript函數系列第三篇——屬性和方法
屬性
【length屬性】
函數系列第二篇中介紹過,arguments對象的length屬性表示實參個數,而函數的length屬性則表示形參個數
function add(x,y){ console.log(arguments.length)//3 console.log(add.length);//2 } add(1,2,3);
【name屬性】
函數定義了一個非標準的name屬性,通過這個屬性可以訪問到給定函數指定的名字,這個屬性的值永遠等于跟在function關鍵字后面的標識符,匿名函數的name屬性為空
//IE11-瀏覽器無效,均輸出undefined //chrome在處理匿名函數的name屬性時有問題,會顯示函數表達式的名字 function fn(){}; console.log(fn.name);//'fn' var fn = function(){}; console.log(fn.name);//'',在chrome瀏覽器中會顯示'fn' var fn = function abc(){}; console.log(fn.name);//'abc'
[注意]name屬性早就被瀏覽器廣泛支持,但是直到ES6才將其寫入了標準
ES6對這個屬性的行為做出了一些修改。如果將一個匿名函數賦值給一個變量,ES5的name屬性,會返回空字符串,而ES6的name屬性會返回實際的函數名
var func1 = function () {}; func1.name //ES5: "" func1.name //ES6: "func1"
如果將一個具名函數賦值給一個變量,則ES5和ES6的name屬性都返回這個具名函數原本的名字
var bar = function baz() {}; bar.name //ES5: "baz" bar.name //ES6: "baz"
Function構造函數返回的函數實例,name屬性的值為“anonymous”
(new Function).name // "anonymous"
bind返回的函數,name屬性值會加上“bound ”前綴
function foo() {}; foo.bind({}).name // "bound foo" (function(){}).bind({}).name // "bound "
【prototype屬性】
每一個函數都有一個prototype屬性,這個屬性指向一個對象的引用,這個對象稱做原型對象(prototype object)。每一個函數都包含不同的原型對象。將函數用做構造函數時,新創建的對象會從原型對象上繼承屬性
function fn(){}; var obj = new fn; fn.prototype.a = 1; console.log(obj.a);//1
方法
【apply()和call()】
每個函數都包含兩個非繼承而來的方法:apply()和call()。這兩個方法的用途都是在特定的作用域中調用函數,實際上等于函數體內this對象的值
要想以對象o的方法來調用函數f(),可以這樣使用call()和apply()
f.call(o);
f.apply(o);
假設o中不存在m方法,則等價于:
o.m = f; //將f存儲為o的臨時方法 o.m(); //調用它,不傳入參數 delete o.m; //將臨時方法刪除
下面是一個實際的例子
window.color = "red"; var o = {color: "blue"}; function sayColor(){ console.log(this.color); } sayColor(); //red sayColor.call(this); //red sayColor.call(window); //red sayColor.call(o); //blue
//sayColor.call(o)等價于: o.sayColor = sayColor; o.sayColor(); //blue delete o.sayColor;
apply()方法接收兩個參數:一個是在其中運行函數的作用域(或者可以說成是要調用函數的母對象,它是調用上下文,在函數體內通過this來獲得對它的引用),另一個是參數數組。其中,第二個參數可以是Array的實例,也可以是arguments對象
function sum(num1, num2){ return num1 + num2; } //因為運行函數的作用域是全局作用域,所以this代表的是window對象 function callSum1(num1, num2){ return sum.apply(this, arguments); } function callSum2(num1, num2){ return sum.apply(this, [num1, num2]); } console.log(callSum1(10,10));//20 console.log(callSum2(10,10));//20
call()方法與apply()方法的作用相同,它們的區別僅僅在于接收參數的方式不同。對于call()方法而言,第一個參數是this值沒有變化,變化的是其余參數都直接傳遞給函數。換句話說,在使用call()方法時,傳遞給函數的參數必須逐個列舉出來
function sum(num1, num2){ return num1 + num2; } function callSum(num1, num2){ return sum.call(this, num1, num2); } console.log(callSum(10,10)); //20
至于是使用apply()還是call(),完全取決于采取哪種函數傳遞參數的方式最方便。如果打算直接傳入arguments對象,或者包含函數中先接收到的也是一個數組,那么使用apply()肯定更方便;否則,選擇call()可能更合適
在非嚴格模式下,使用函數的call()或apply()方法時,null或undefined值會被轉換為全局對象。而在嚴格模式下,函數的this值始終是指定的值
var color = 'red'; function displayColor(){ console.log(this.color); } displayColor.call(null);//red
var color = 'red'; function displayColor(){ 'use strict'; console.log(this.color); } displayColor.call(null);//TypeError: Cannot read property 'color' of null
應用
【1】調用對象的原生方法
var obj = {}; obj.hasOwnProperty('toString');// false obj.hasOwnProperty = function (){ return true; }; obj.hasOwnProperty('toString');// true Object.prototype.hasOwnProperty.call(obj, 'toString');// false
【2】找出數組最大元素
javascript不提供找出數組最大元素的函數。結合使用apply方法和Math.max方法,就可以返回數組的最大元素
var a = [10, 2, 4, 15, 9]; Math.max.apply(null, a);//15
【3】將類數組對象轉換成真正的數組
Array.prototype.slice.apply({0:1,length:1});//[1]
或者
[].prototype.slice.apply({0:1,length:1});//[1]
【4】將一個數組的值push到另一個數組中
var a = []; Array.prototype.push.apply(a,[1,2,3]); console.log(a);//[1,2,3] Array.prototype.push.apply(a,[2,3,4]); console.log(a);//[1,2,3,2,3,4]
如果使用ES6中的不定參數則非常簡單
var a = [...[1,2,3],...[2,3,4]]; console.log(a);//[1,2,3,2,3,4]
【5】綁定回調函數的對象
由于apply方法(或者call方法)不僅綁定函數執行時所在的對象,還會立即執行函數,因此不得不把綁定語句寫在一個函數體內。更簡潔的寫法是采用下面介紹的bind方法
var o = {}; o.f = function () { console.log(this === o); } var f = function (){ o.f.apply(o); }; $('#button').on('click', f);
【bind()】
bind()是ES5新增的方法,這個方法的主要作用就是將函數綁定到某個對象
當在函數f()上調用bind()方法并傳入一個對象o作為參數,這個方法將返回一個新的函數。以函數調用的方式調用新的函數將會把原始的函數f()當做o的方法來調用,傳入新函數的任何實參都將傳入原始函數
[注意]IE8-瀏覽器不支持
function f(y){ return this.x + y; //這個是待綁定的函數 } var o = {x:1};//將要綁定的對象 var g = f.bind(o); //通過調用g(x)來調用o.f(x) g(2);//3
兼容代碼
Function.prototype.bind = function(context){ var self = this; return function(){ return self.apply(context,arguments); } }
通常,會把它實現得稍微復雜一點,使得可以填入一些參數
Function.prototype.bind = function(context){ var self = this, context = [].shift.call(arguments), args = [].slice.call(arguments); return function(){ return self.apply(context,[].concat.call(args,[].slice.call(arguments))); } }
bind()方法不僅是將函數綁定到一個對象,它還附帶一些其他應用:除了第一個實參之外,傳入bind()的實參也會綁定到this,這個附帶的應用是一種常見的函數式編程技術,有時也被稱為'柯里化'(currying)
var sum = function(x,y){ return x+y; } var succ = sum.bind(null,1); succ(2); //3,x綁定到1,并傳入2作為實參y
function f(y,z){ return this.x + y + z; } var g = f.bind({x:1},2); g(3); //6,this.x綁定到1,y綁定到2,z綁定到3
使用bind()方法實現柯里化可以對函數參數進行拆分
function getConfig(colors,size,otherOptions){ console.log(colors,size,otherOptions); } var defaultConfig = getConfig.bind(null,'#c00','1024*768'); defaultConfig('123');//'#c00 1024*768 123' defaultConfig('456');//'#c00 1024*768 456'
【toString()】
函數的toString()實例方法返回函數代碼的字符串,而靜態toString()方法返回一個類似'[native code]'的字符串作為函數體
function test(){ alert(1);//test } test.toString();/*"function test(){ alert(1);//test }"*/ Function.toString();//"function Function() { [native code] }"
【toLocaleString()】
函數的toLocaleString()方法和toString()方法返回的結果相同
function test(){ alert(1);//test } test.toLocaleString();/*"function test(){ alert(1);//test }"*/ Function.toLocaleString();//"function Function() { [native code] }"
【valueOf()】
函數的valueOf()方法返回函數本身
function test(){ alert(1);//test } test.valueOf();/*function test(){ alert(1);//test }*/ typeof test.valueOf();//'function' Function.valueOf();//Function() { [native code] }
參考資料
【1】 W3School-Javascript高級教程——Function對象 http://www.w3school.com.cn/js/
【2】 阮一峰Javascript標準參考教程——函數的屬性和方法 http://javascript.ruanyifeng.com/grammar/
【3】《javascript權威指南(第6版)》第8章 函數
【4】《javascript高級程序設計(第3版)》第5章 引用類型
【5】《javascript DOM編程藝術(第2版)》第2章 javascript語法
【6】《javascript語句精粹》第4章 函數
文章列表