你真的懂函數嗎?

函數聲明方式

匿名函數

function后面直接跟括號,中間沒有函數名的就是匿名函數。

let fn = function() {console.log('我是fn')
}
let fn2 = fn
console.log(fn.name) //fn
console.log(fn2.name)//fn,fn和fn2指向的是同一個function。
復制代碼

具名函數

function后面有函數名字的,不是直接跟括號的的就是具名函數。 如果把一個具名函數賦值給一個變量,那么這個具名函數的作用域就不是window了。

let fn = function fn1() {console.log('function')
}
console.log(fn.name) //fn1
console.log(fn1,name) // ReferenceError: fn1 is not defined
復制代碼

箭頭函數

箭頭函數是es6知識點,具有以下幾個特點:

  1. 如果只有一個參數,可以省略小括號。
  2. 如果有至少有兩個參數,必須加小括號。
  3. 如果函數體只有一句話可以省略花括號,并且這一句作為返回值return。
  4. 如果函數體至少有兩句必須加上花括號。
  5. 箭頭函數里面是沒有this的。
let fn = e => e+1
console.log(fn(1)) //2let fn1 = (i,y) => i+y
console.log(fn1(2,3)) //5let fn2 = (i,y) => {i+=1;y+=2;return i+y
}
console.log(fn2(5,6)) //13
復制代碼

詞法作用域(靜態作用域)

靜態作用域又叫做詞法作用域,采用詞法作用域的變量叫詞法變量。詞法變量有一個在編譯時靜態確定的作用域。詞法變量的作用域可以是一個函數或一段代碼,該變量在這段代碼區域內可見(visibility);在這段區域以外該變量不可見(或無法訪問)。詞法作用域里,取變量的值時,會檢查函數定義時的文本環境,捕捉函數定義時對該變量的綁定。

詞法作用域:變量的作用域是在定義時決定而不是執行時決定,也就是說詞法作用域取決于源碼,通過靜態分析就能確定,因此詞法作用域也叫做靜態作用域。 with和eval除外,所以只能說JS的作用域機制非常接近詞法作用域(Lexical scope)。

通過詞法作用域樹能判斷變量指向關系,但是不能斷定變量的值,變量的值還需要根據執行順序進一步作出判斷,看一下例子:

因為JavaScript采用的是詞法作用域,bian'liang的作用域基于函數創建的位置,跟調用時的位置無關。

var i = 1,j = 2,k = 3;function a(o, p, x, q) {var x = 4;alert(i);function b(r, s) {var i = 11,y = 5;alert(i);function c(t) {var z = 6;alert(i);};var d = function() {alert(y);};c(60);d();};b(40, 50);
}
a(10, 20, 30); //1 11 11 5
復制代碼
/**
* 模擬建立一棵語法分析樹,存儲function內的變量和方法
*/
var SyntaxTree = {// 全局對象在語法分析樹中的表示window: {variables:{i:{ value:1},j:{ value:2},k:{ value:3}},functions:{a: this.a}},a:{variables:{x:'undefined'},functions:{b: this.b},scope: this.window},b:{variables:{i:'undefined'y:'undefined'},functions:{c: this.c,d: this.d},scope: this.a},c:{variables:{z:'undefined'},functions:{},scope: this.b},d:{variables:{},functions:{},scope: {scope: this.b}}
};
復制代碼
/**
* 活動對象:函數執行時創建的活動對象列表
*/
let ActiveObject = {window: {variables: {i: { value: 1 }j: { value: 2 }k: { value: 3 }},functions: {a: this.a}}a: {variables: {x: { vale: 4 },functions: {b: this.b},scope: this.window,params: {o: { value: 10 },p: { value: 20 },x: this.variables.xq: { vale: 'undefined' }},arguments: [this.params.o, this.params.p, this.params.x]}}b: {variables: {i: { vale: 11 },y: { vale: 5 },},functions: {c: this.c,d: this.d},params: {r: { value: 40 }s: { value: 50 }},arguments: [this.params.r, this.params.scope]scope: this.a}c: {variables: {z: { value: 6 },functions: {},params: {t: { value: 60 }},arguments: [this.params.t]scope: this.b}}d: {variables: {},functions: {},params: {},arguments: []this.scope: this.b}}
復制代碼

call stack

進入call stack 時的一些規則:

  1. 函數的所有形參(如果我們是在函數執行上下文中)

    • 由名稱和對應值組成的一個變量對象的屬性被創建;沒有傳遞對應參數的話,那么由名稱和 undefined 值組成的一種變量對象的屬性也將被創建。
  2. 所有函數聲明(FunctionDeclaration, FD)

    • 由名稱和對應值(函數對象(function-object))組成一個變量對象的屬性被創建;如果變量對象已經存在相同名稱的屬性,則完全替換這個屬性。
  3. 所有變量聲明(var, VariableDeclaration)

    • 由名稱和對應值(undefined)組成一個變量對象的屬性被創建;如果變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會干擾已經存在的這類屬性。
/*
* example1:形參
*/
function test(a, b) {/*var a = 10var b = undefined根據規則1,在進入執行上下文時會自動對形參聲明并且賦值。*/ console.log(a)var c = 10;function d() {}var e = function _e() {};(function x() {});
}
test(10); // 10
復制代碼
/*
* example2:函數聲明
*/
function test(a, b) {console.log(a)function a() {}var e = function _e() {};
}
test(10); // ? a() {} .根據規則2,進入執行上下文會自動聲明形參并且賦值,但是同名的函數聲明會替換這個變量。function test(a, b) {console.log(a)var a = 30;var a = function _e() {};
}
test(10); // 10 .根據規則2,進入執行上下文會自動聲明形參并且賦值,但是同名的函數聲明會替換這個變量。
復制代碼
/*
* example3:變量聲明
*/
console.log(foo);//會打印出foo函數,根據規則3,同名的變量聲明不會干擾函數聲明和形參function foo(){console.log("foo");
}var foo = 1;
復制代碼

this和arguments

函數調用

在es5中,函數有四種調用方式:

1. fn(p1,p2)
2. obj.fn(p1,p2)
3. fn.call(context,p1,p2)
4. fn.apply(context,p1,p2)
復制代碼

第三和第四種才是正常的js函數調用方式,其他兩種就是語法糖。

fn(p1,p2)     等價于 fn.call(undefined,p1,p2) 等價于 fn.apply(context,[p1,p2])
obj.fn(p1,p2) 等價于 obj.fn.call(obj,p1,p2)   等價于 obj.fn.apply(obj,[p1,p2])
復制代碼

如果你傳的 context 就 null 或者 undefined,那么 window 對象就是默認的 context(嚴格模式下默認 context 是 undefined)

this是什么??

this是call的第一個參數!!!!

var obj = {foo: function(){console.log(this)}
}var bar = obj.foo
obj.foo() // 打印出的 this 是 obj
bar() // 打印出的 this 是 window
復制代碼
obj.foo() 相當于 obj.foo.call(obj) 也就相當于把函數名前面的作為call的第一個參數,也就是this,如果沒有就是window。
bar() 相當于 bar.call(undefined) 
復制代碼

在執行函數的時候,this是隱藏的一個參數,且必須是一個對象,如果不是,js是自動把它轉為對象。

function fn() {console.log(this)console.log(arguments)
}
fn.call(1,2,3) // Number?{1}  [2,3]
復制代碼

arguments

arguments是偽數組它類似于Array,但除了length屬性和索引元素之外沒有任何Array屬性。 call和apply里面除了第一個參數之外的都是arguments,如果arguments的個數少建議使用call,使用apply也可以,如果不確定就使用apply。 使用一下方法吧arguments轉為真正的數組:

var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);// ES2015
const args = Array.from(arguments);
const args = [...arguments]
復制代碼

bind

MDN 官方文檔對 bind() 的定義:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

大概意思就是,bind會返回一個新的函數(并沒有的調用原來的函數),這個新函數會call原來的函數,call的參數由你決定。看例子:

		this.x = 9;var module = {x: 81,getX: function() { return this.x; }};var retrieveX = module.getX;var boundGetX = retrieveX.bind(module);boundGetX(); // 81
復制代碼

retrieveX.bind(module)返回了一個新的函數boundGetX,然后調用這個新的函數的時候,把這個函數里面的this綁定到了module對象上,所以this.x就相當于module.x也就是等于81.

柯里化

在計算機科學中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數而且返回結果的新函數的技術。這個技術由克里斯托弗·斯特雷奇以邏輯學家哈斯凱爾·加里命名的,盡管它是Moses Sch?nfinkel和戈特洛布·弗雷格發明的。

說的明白一點就是,給函數傳遞一部分參數,讓它返回一個函數去處理其他參數,舉個例子,求三個數之和:

let addOne = function add(x) {return function(y) {return function(z) {return x+y+z}}
}let one = addOne(3)
console.log(one)//? (y) {return function (z) {return x + y + z}}
let two = one(4)
console.log(two)//? (z) {return x + y + z}
let three = two(5)
console.log(three)//12
復制代碼

javascript函數柯里化--詳細說明鏈接

高階函數

在數學和計算機科學中,高階函數是至少滿足下列一個條件的函數:

  1. 接受一個或多個函數作為輸入
  2. 輸出一個函數

舉一些高階函數的例子:

/*
*接受一個或多個函數作為輸入
*/
1. Array.prototype.filter()
2. Array.prototype.forEach()
3. Array.prototype.reduce()
4. Array.prototype.map()
5. Array.prototype.find()
6. Array.prototype.every()
復制代碼
/*
*輸出一個函數
*/
1. fn.bind(args)
復制代碼

回調函數

函數A作為參數(函數引用)傳遞到另一個函數B中,并且這個函數B執行函數A。我們就說函數A叫做回調函數。如果沒有名稱(函數表達式),就叫做匿名回調函數。

名詞形式:被當做參數的函數就是回調 動詞形式:調用這個回調 注意回調跟異步沒有任何關系

回調函數的使用場合

  1. 資源加載:動態加載js文件后執行回調,加載iframe后執行回調,ajax操作回調,圖片加載完成執行回調,AJAX等等。

  2. DOM事件及Node.js事件基于回調機制(Node.js回調可能會出現多層回調嵌套的問題)。

  3. setTimeout的延遲時間為0,這個hack經常被用到,settimeout調用的函數其實就是一個callback的體現

  4. 鏈式調用:鏈式調用的時候,在賦值器(setter)方法中(或者本身沒有返回值的方法中)很容易實現鏈式調用,而取值器(getter)相對來說不好實現鏈式調用,因為你需要取值器返回你需要的數據而不是this指針,如果要實現鏈式方法,可以用回調函數來實現。

  5. setTimeout、setInterval的函數調用得到其返回值。由于兩個函數都是異步的,即:他們的調用時序和程序的主流程是相對獨立的,所以沒有辦法在主體里面等待它們的返回值,它們被打開的時候程序也不會停下來等待,否則也就失去了setTimeout及setInterval的意義了,所以用return已經沒有意義,只能使用callback。callback的意義在于將timer執行的結果通知給代理函數進行及時處理。

回調函數的傳遞

傳遞的方式有兩種,函數引用和函數表達式。

$.get('myhtmlpage.html', myCallBack);//這是對的
$.get('myhtmlpage.html', myCallBack('foo', 'bar'));//這是錯的,那么要帶參數呢?
$.get('myhtmlpage.html', function(){//帶參數的使用函數表達式
myCallBack('foo', 'bar');
});
復制代碼

箭頭函數與es5的函數主要區別

箭頭函數的主要區別在this,箭頭函數是沒有this這個概念的,看例子:

setTimeout(function(a){console.log(this) //這個this指的是{name:'Jack'}setTimeout(function(a){console.log(this) //這個this指的是window,因為沒有bind,調用setTimeout的是window},1000)
}.bind({name:'Jack'}),1000)
復制代碼
setTimeout(function(a){console.log(this) //這個this指的是{name:'Jack'}setTimeout(function(a){console.log(this) //這個this指的是{name: "Jack"},因為bind了外面的this也就是{name: "Jack"}},1000)
}.bind({name:'Jack'}),1000)
復制代碼
setTimeout(function(a){console.log(this) //這個this指的是{name:'Jack'}setTimeout(a=>console.log(this),1000)//這個this指的是{name:'Jack'},因為箭頭函數沒有this的概念,它指的this就是外面的this,也就是{name:'Jack'}
}.bind({name:'Jack'}),1000)
復制代碼

至此基本上說了js的所有函數內容,只是簡單舉個例子,更深入的研究還需要看一些其他大佬的博客哦~~~~

轉載于:https://juejin.im/post/5aa5ad0af265da237a4ca4f5

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452418.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452418.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452418.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

靜態html的ajax如何發請求,靜態頁面ajax - 冥焱的個人空間 - OSCHINA - 中文開源技術交流社區...

1.靜態頁面$.ajax({type:"get",url:"http://localhost:8080/app/register/sendSMS",//請求地址必須帶http協議data:{"phone":phone},async:false,//是否異步dataType: "jsonp",//固定格式jsonp: "callback",//固定格式jsonp…

Diango博客--12.開發 Django 博客文章閱讀量統計功能

文章目錄0.models中增加新字段1.models中增加方法2.遷移數據庫3.修改視圖函數4.在模板中顯示閱讀量0.models中增加新字段 為了記錄文章的瀏覽量,需要在文章的數據庫表中新增一個用于存儲閱讀量的字段。 文件位置:blog/models.py class Post(models.Mo…

c++ try_catch throw

使用throw拋出異常 本人節選自《21天學通C》一書 拋出異常(也稱為拋棄異常)即檢測是否產生異常,在C中,其采用throw語句來實現,如果檢測到產生異常,則拋出異常。該語句的格式為: throw 表達式…

數字證書和數字簽名

什么是數字證書?由于Internet網電子商務系統技術使在網上購物的顧客能夠極其方便輕松地獲得商家和企業的信息,但同時也增加了對某些敏感或有價值的數據被濫用的風險. 為了保證互聯網上電子交易及支付的安全性,保密性等,防范交易及支付過程中的欺詐行為&a…

域名劫持

轉載于:https://www.cnblogs.com/xinghen1216/p/8548323.html

cesium html源碼,Cesium源碼的本地運行及調試

CesiumJS源碼運行有兩種方式:基于node.js運行官方下載地址:https://cesium.com/cesiumjs/下載解壓后,在根目錄安裝依賴后,就可直接運行npm initnpm start如果調試代碼呢,官方的示例都是在Sandcastle里放著,…

Diango博客--13.將“視圖函數”類轉化為“類視圖”

文章目錄0.思路引導1.ListView2.將 index 視圖函數改寫為類視圖3.將 category 視圖函數改寫為類視圖4.將 archive 視圖函數改寫成類視圖5.將 tag 視圖函數改寫成類視圖6.DetailView7.將DetailView視圖函數改寫成類視圖0.思路引導 1)在開發網站的過程中,…

es6之數據結構 set,WeakSet,mapWeakMap

{let list new Set();list.add(1);list.add(2);list.add(1);console.log(list); //Set(2) {1, 2} let arr[1,2,3,1,2] let list2new Set(arr); console.log(list2); //Set(3) {1, 2, 3} } Set ES6 提供了新的數據結構 Set。它類似于數組,但是成員的值都是唯一的&a…

關于C語言中繼承和多態的實現

以下的內容綜合了多篇文章,加上一點自己的理解而成。目的為了給自己閱讀他們文章后做一個筆記。在末尾給出了這些文章的地址。 多態的實現可以采用以下幾種方式: (1)使用 vod * (萬能指針)來實現“編譯時多態”。 (2)使用函數指針來實現“運行時…

邊界測試

邊界條件邊界測試是單元測試中最后的也可能是最重要的任務。 軟件常常在它的邊界上失效,例如,處理n元數組的第n個元素時,或做到i次循環中的第i次重復時,往往會發生錯誤。 使用剛好小于、剛好等于和剛好大于最大值或最小值的數據結…

阿里云上Kubernetes集群聯邦

摘要: kubernetes集群讓您能夠方便的部署管理運維容器化的應用。但是實際情況中經常遇到的一些問題,就是單個集群通常無法跨單個云廠商的多個Region,更不用說支持跨跨域不同的云廠商。這樣會給企業帶來一些擔憂,如何應對可用區級別…

缺氧游戲計算機,缺氧PC最低什么配置一覽 你覺得高嗎

缺氧PC最低什么配置一覽,你覺得高嗎。游戲對于電腦有不同程度的要求,缺氧這款游戲也有著自己的配置要求,看看下面的缺氧PC最低什么配置一覽,你的硬件夠得上嗎。缺氧最低配置:首先公布的是官 方配置需求,目前…

Diango博客--14.使用 Django 項目中的 ORM 編寫偽造測試數據腳本

文章目錄0.思路引導1.腳本目錄結構2.使用 Faker 快速生成測試數據3.批量生成測試數據4.執行腳本5.效果展示0.思路引導 1)為了防止博客首頁展示的文章過多以及提升加載速度,可以對文章列表進行分頁展示。 2)不過這需要比較多的文章才能達到分…

基于Sql Server 2008的分布式數據庫的實踐

配置Sql Server 2008(Win7) 1.打開SQL server2012,使用windows身份登錄 2.登錄后,右鍵選擇“屬性”。左側選擇“安全性”,選中右側的“SQL Server 和 Windows 身份驗證模式”以啟用混合登錄模式 3.選擇“連接”&#x…

橫向技術分析C#、C++和Java優劣

本文將從技術人員的角度橫向分析C#、C和Java優劣,其實選擇Java陣營還是.NET陣營,大家可以根據自己的實際需要來確定。 C#誕生之日起,關于C#與Java之間的論戰便此起彼伏,至今不輟。拋卻Microsoft與Sun之間的恩怨與口角,…

軟件測試中的存根程序

存根程序用來代替被測試的模塊所調用的模塊,因此存根程序也稱為“虛擬子程序”,它利用被它代替的模塊的接口,只做盡可能少的數據操作。

計算機網絡應用云計算,計算機網絡云計算的類型

原標題:計算機網絡云計算的類型隨著現代計算機網絡技術的不斷發展,越來越多的與計算機網絡有關的現代化技術得以出現,并且有著廣泛的應用,其中云計算技術就是比較常見的一種,在實際應用中發揮著較高的價值。在信息時代…

sublime_text快捷鍵

1、注釋:選中文本后,CTRL / 2、CTRL N,CTRLS,保存成.html文件后,只需要輸入感嘆號!,然后tab鍵,即可打印出基本的html格式!轉載于:https://www.cnblogs.com/JAVA-STUDYER/p/855040…

Diango博客--15.通過 Django Pagination 實現簡單分頁(一)

文章目錄0.思路引導1.Paginator 類的常用方法2.用 Paginator 給文章列表分頁3.在模板中設置分頁導航4.效果展示0.思路引導 1)當博客上發布的文章越來越多時,通常需要進行分頁顯示,以免所有的文章都堆積在一個頁面,影響用戶體驗。…

SpringMVC 測試 mockMVC

SpringMVC測試框架 基于RESTful風格的SpringMVC的測試,我們可以測試完整的Spring MVC流程,即從URL請求到控制器處理,再到視圖渲染都可以測試。 一 MockMvcBuilder MockMvcBuilder是用來構造MockMvc的構造器,其主要有兩個實現&…