Js基礎知識5-函數返回值、函數參數、函數屬性、函數方法

函數返回值

全部函數都有返回值,沒有return語句時,默認返回內容爲undefined,和其餘面向對象的編程語言同樣,return語句不會阻止finally子句的執行。javascript

function testFinnally(){ try{ return 2; }catch(error){ return 1; }finally{ return 0; } } testFinnally();//0

若是函數調用時在前面加上了new前綴,且返回值不是一個對象,則返回this(該新對象)。java

function fn(){ this.a = 2; return 1; } var test = new fn(); console.log(test);//{a:2}
console.log(test.constructor);//fn(){this.a = 2;return 1;}

若是返回值是一個對象,則返回該對象。c++

function fn(){ this.a = 2; return {a:1}; } var test = new fn(); console.log(test);//{a:1}
console.log(test.constructor);//Object() { [native code] }

函數參數

argumentschrome

  javascript中的函數定義並未指定函數形參的類型,函數調用也未對傳入的實參值作任何類型檢查。實際上,javascript函數調用甚至不檢查傳入形參的個數。編程

function add(x){
    return x+1;
}
console.log(add(1));//2
console.log(add('1'));//'11'
console.log(add());//NaN
console.log(add(1,2));//2

同名形參數組

  在非嚴格模式下,函數中能夠出現同名形參,且只能訪問最後出現的該名稱的形參。瀏覽器

function add(x,x,x){ return x; } console.log(add(1,2,3));//3

     而在嚴格模式下,出現同名形參會拋出語法錯誤app

function add(x,x,x){ 'use strict'; return x; } console.log(add(1,2,3));//SyntaxError: Duplicate parameter name not allowed in this context

參數個數dom

  當實參比函數聲明指定的形參個數要少,剩下的形參都將設置爲undefined值編程語言

function add(x,y){ console.log(x,y);//1 undefined } add(1);

       經常使用邏輯或運算符給省略的參數設置一個合理的默認值

function add(x,y){ y = y || 2; console.log(x,y);//1 2 } add(1);

  [注意]實際上,使用y || 2是不嚴謹的,顯式地設置假值(undefined、null、false、0、-0、」、NaN)也會獲得相同的結果。因此應該根據實際場景進行合理設置

  當實參比形參個數要多時,剩下的實參沒有辦法直接得到,須要使用即將提到的arguments對象

  javascript中的參數在內部用一個數組表示。函數接收到的始終都是這個數組,而不關心數組中包含哪些參數。在函數體內能夠經過arguments對象來訪問這個參數數組,從而獲取傳遞給函數的每個參數。arguments對象並非Array的實例,它是一個類數組對象,可使用方括號語法訪問它的每個元素

function add(x){ console.log(arguments[0],arguments[1],arguments[2])//1 2 3 return x+1; } add(1,2,3);

      arguments對象的length屬性顯示實參的個數,函數的length屬性顯示形參的個數

function add(x,y){ console.log(arguments.length)//3 return x+1; } add(1,2,3); console.log(add.length);//2

     形參只是提供便利,但不是必需的

function add(){ return arguments[0] + arguments[1]; } console.log(add(1,2));//3

對象參數

  當一個函數包含超過3個形參時,要記住調用函數中實參的正確順序實在讓人頭疼

function arraycopy(/*array*/from,/*index*/form_start,/*array*/to,/*index*/to_start,/*integer*/length){ //todo }

      經過名/值對的形式來傳入參數,這樣參數的順序就可有可無了。定義函數的時候,傳入的實參都寫入一個單獨的對象之中,在調用的時候傳入一個對象,對象中的名/值對是真正須要的實參數據

function easycopy(args){ arraycopy(args.from,args.from_start || 0,args.to,args.to_start || 0, args.length); } var a = [1,2,3,4],b =[]; easycopy({from:a,to:b,length:4});

以函數爲參數

      函數自己是一個對象,所以能夠將函數做爲另外一個函數的參數,進而實現函數回調,功能等同於c++中的函數指針

function printf(str){ dom1.innerText += str.toString()+"\n"; //設置dom1顯示的文字。變量也能夠自動調用其餘js文件中的dom1變量。dom1會先在當前文件中查詢,而後向以前引用的js文件查詢,再向以後引用的js文件查詢 } function callfunction(myfunction,myargument){ //函數做爲其餘函數的參數 return myfunction(myargument); //調用回調函數 } callfunction(printf,"hello world");

同步

  當形參與實參的個數相同時,arguments對象的值和對應形參的值保持同步

function test(num1,num2){ console.log(num1,arguments[0]);//1 1 arguments[0] = 2; console.log(num1,arguments[0]);//2 2 num1 = 10; console.log(num1,arguments[0]);//10 10 } test(1);

  [注意]雖然命名參數和對應arguments對象的值相同,但並非相同的命名空間。它們的命名空間是獨立的,但值是同步的

  但在嚴格模式下,arguments對象的值和形參的值是獨立的

function test(num1,num2){ 'use strict'; console.log(num1,arguments[0]);//1 1 arguments[0] = 2; console.log(num1,arguments[0]);//1 2 num1 = 10; console.log(num1,arguments[0]);//10 2 } test(1);

      當形參並無對應的實參時,arguments對象的值與形參的值並不對應

function test(num1,num2){ console.log(num1,arguments[0]);//undefined,undefined num1 = 10; arguments[0] = 5; console.log(num1,arguments[0]);//10,5 } test();

內部屬性【callee】

  arguments對象有一個名爲callee的屬性,該屬性是一個指針,指向擁有這個arguments對象的函數

  下面是經典的階乘函數

function factorial(num){ if(num <=1){ return 1; }else{ return num* factorial(num-1); } } console.log(factorial(5));//120

      但在嚴格模式下,訪問這個屬性會拋出TypeError錯誤

function factorial(num){ 'use strict'; if(num <=1){ return 1; }else{ return num* arguments.callee(num-1); } } //TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them console.log(factorial(5));

     這時,可使用具名的函數表達式

var factorial = function fn(num){ if(num <=1){ return 1; }else{ return num*fn(num-1); } }; console.log(factorial(5));//120

【caller】

  實際上有兩個caller屬性

【1】函數的caller

  函數的caller屬性保存着調用當前函數的函數的引用,若是是在全局做用域中調用當前函數,它的值是null

function outer(){ inner(); } function inner(){ console.log(inner.caller);//outer(){inner();}
} outer();
function inner(){ console.log(inner.caller);//null
} inner();

     在嚴格模式下,訪問這個屬性會拋出TypeError錯誤

function inner(){ 'use strict'; //TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
 console.log(inner.caller); } inner();

【2】arguments對象的caller

  該屬性始終是undefined,定義這個屬性是爲了分清arguments.caller和函數的caller屬性

function inner(x){ console.log(arguments.caller);//undefined
} inner(1);

      一樣地,在嚴格模式下,訪問這個屬性會拋出TypeError錯誤

function inner(x){ 'use strict'; //TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
 console.log(arguments.caller); } inner(1);

函數重載

  javascript函數不能像傳統意義上那樣實現重載。而在其餘語言中,能夠爲一個函數編寫兩個定義,只要這兩個定義的簽名(接受的參數的類型和數量)不一樣便可

  javascript函數沒有簽名,由於其參數是由包含0或多個值的數組來表示的。而沒有函數簽名,真正的重載是不可能作到的

//後面的聲明覆蓋了前面的聲明
function addSomeNumber(num){ return num + 100; } function addSomeNumber(num){ return num + 200; } var result = addSomeNumber(100);//300

      只能經過檢查傳入函數中參數的類型和數量並做出不一樣的反應,來模仿方法的重載

function doAdd(){ if(arguments.length == 1){ alert(arguments[0] + 10); }else if(arguments.length == 2){ alert(arguments[0] + arguments[1]); } } doAdd(10);//20
doAdd(30,20);//50

參數傳遞

  javascript中全部函數的參數都是按值傳遞的。也就是說,把函數外部的值複製到函數內部的參數,就和把值從一個變量複製到另外一個變量同樣

【1】基本類型值

  在向參數傳遞基本類型的值時,被傳遞的值會被複制給一個局部變量(命名參數或arguments對象的一個元素)

function addTen(num){ num += 10; return num; } var count = 20; var result = addTen(count); console.log(count);//20,沒有變化
console.log(result);//30

【2】引用類型值

  在向參數傳遞引用類型的值時,會把這個值在內存中的地址複製給一個局部變量,所以這個局部變量的變化會反映在函數的外部

function setName(obj){ obj.name = 'test'; } var person = new Object(); setName(person); console.log(person.name);//'test'

     當在函數內部重寫引用類型的形參時,這個變量引用的就是一個局部對象了。而這個局部對象會在函數執行完畢後當即被銷燬

function setName(obj){ obj.name = 'test'; console.log(person.name);//'test'
    obj = new Object(); obj.name = 'white'; console.log(person.name);//'test'
} var person = new Object(); setName(person);

函數屬性

【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]

【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 bind(f,o){ if(f.bind){ return f.bind(o); }else{ return function(){ return f.apply(o,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] }

 原鏈:https://blog.csdn.net/luanpeng825485697/article/details/77010261

相關文章
相關標籤/搜索