javascript函數以及原型對象

函數定義

函數的定義可以通過function關鍵字.也可以通過表達式

function show1(){}
let show2 = function(){}//匿名函數
show1()
show2()//通過變量調用

函數也可以通過內置的javascript函數構造器:Function()定義.但是實際上不必使用函數構造器(構造函數)來創建函數

//構造器創建
let show = new Function("a","b","return a+b")
console.log(show(1,2))//3
//不使用構造器
let show = function(a,b){return a+b}
console.log(show(1,2))//3

小提示:在javascript中很多時候要避免使用new關鍵字

函數支持函數提升,即函數調用在函數定義之前.注意函數表達式不支持函數提升

函數是對象

雖然使用 typeof判斷函數類型返回的是 “function”.但是將函數認爲是對象更加合適
函數有屬性和方法.比如toString()方法

函數裏面還內置了一個arguments對象,通過該對象可以獲取有關參數的值.

創建對象

創建對象方式有兩種,第一種:直接創建實例對象.第二種通過構造函數創建對象

//直接創建對象實例
let person = {
	name:'陳林',
	age:18,
	function show(){}//對象的方法
}
//通過構造函數創建對象
function Person(name,age){
	this.name = name
	this.age = age
}
let person = new Person("陳林",18)

額.顯然這裏的函數被稱爲構造函數.函數被用來創建對象時,稱爲構造函數.函數寫在對象內部,便稱爲方法.

函數裏面this的指向問題

一般來說 this指向函數執行時的當前對象,也就是誰來調用了這個函數
當函數沒有被自身對象調用時,this就會指向全局對象window

有了構造函數的概念後,下面就講原型對象

原型對象

首先假如構造函數被定義好後,再往構造函數裏面添加方法.然後創建對象.,其創建的對象就不能訪問到添加的方法

function Show(name,age){//構造函數
  this.name = name
}
Show.age =function(){console.log('年齡')}
let show = new Show("陳林")
console.log(show.age)//undefined

但是通過這樣,卻可以讓對象訪問到

function Show(name,age){//構造函數
  this.name = name
}
Show.prototype.age =function(){console.log('年齡')}
let show = new Show("陳林")
console.log(show.age)//顯示方法

每一個函數(包括構造函數)都有prototype屬性都會指向一個原型對象.而每一個實例對象都有一個__proto__屬性同樣指向一個原型對象.它們兩個指向的原型對象就是同一個.

補充一下,原型對象中也有一個constructor屬性來指向函數(構造函數).所以關係爲:構造函數通過prototype指向原型對象,而原型對象通過constructor指向構造函數.互相可以訪問.

備註: 原型對象可以看做一個空的object對象.這個的空可以理解爲我們沒有往裏面添加屬性或者方法.這裏的實例對象看作是根據構造函數而創建的.
在這裏插入圖片描述
基本流程爲: 構造函數Person()和實例對象person都指向同一個原型對象.但是原型對象也是對象,是一個空的object實例對象.所以堆空間一開始就有Object()構造函數對象.來創建這個空的object實例對象.這樣它們兩個就同時指向它們的原型對象(Object.prototype).但是最終這個Object構造函數的原型對象的__proto__值爲null,原型鏈結束.

對應代碼:

function Person(name) {
  this.name = name
}

let person = new Person("陳林")
Person.age = function () {
  console.log('年齡')
}

person.age()// 年齡

顯然,通過Person.prototype.age=function(){}來往原型對象中增加方法.其實例對象person也就可以通過__proto__屬性來訪問到了.因爲實例對象和構造函數都指向同一個原型對象.

原型鏈

當在一個對象中調用一個方法時,首先會查看這個方法是否在本對象中,如果沒有則會往該對象的原型對象裏面找(通過__proto__),如果該對象的原型對象中也沒有,會再往原型對象的原型對象中查.直到找到Object的原型對象.如果Object的原型對象裏面也沒有則返回undefined(因爲Object的原型對象中__proto__null).

這樣就構成了一條__proto__鏈.稱爲原型鏈.

注意,一般原型鏈中可以加入方法,也可以讀取方法.但是對於對象的屬性來說:讀取屬性時,會往原型鏈中查找,而添加屬性時,不會查找原型鏈(本對象有屬性則覆蓋,沒屬性則添加).

一般通過prototype來添加方法,而不添加屬性,並且通常不會通過__proto__來往原型鏈中添加

完整原型鏈

在這裏插入圖片描述
解析: 首先是創建f1實例對象,所以實例對象與Foo()構造函數同時指向Foo.prototype.又因爲Foo.prototype也是Object的實例對象,所以與Object()構造函數同時指向Object.prototype.
然後是Foo()構造函數也是Function的實例對象,所以Foo()Function()構造函數同時指向Function.prototype.

最後是幾處特別的:
圖中創建o1 o2Object的實例對象,所以通過__proto__來指向Object.prototype

圖中Function()構造函數既有prototype指向Function.prototype,又有__proto__來指向Function.prototype. 說明Function()構造函數既是實例對象,又是構造函數.所以Function()構造函數可以表示爲let Function = new Function()

圖中Object()構造函數也是Function的實例對象,所以通過__proto__來指向Function.prototype.

順便補充一下instanceof判斷依據

表達式 A instanceof B

如果B函數的prototype(B的原型對象) 在A對象的原型鏈上,返回true 否則返回false

總結一下

一切對象都是Object的實例,一切函數都是Function的實例對象
構造函數通過 prototype屬性訪問原型對象.
實例對象通過 __proto__內部屬性訪問原型對象

js調用函數時,傳遞參數時,是值傳遞還是引用傳遞

理解1: 都是值傳遞,當傳遞的是基本類型時,傳遞的是值.當傳遞的是引用類型時,傳遞的也是值,只不過這個值是地址而已.

理解2: 基本類型是值傳遞,引用類型是引用傳遞.

對此需要特別強調:


let a = {name:'chenlin'}
let b = a
a= {name:'test'}
console.log(a.name)//test
console.log(b.name)//chenlin

//說明:當兩個變量指向同一個對象時,假如其中一個對象指向了另外一個對象時,兩個變量就沒有干係了!!!
//只有修改對象的值纔會使兩個值都會改變.
let a = {name:'chenlin'}
let b = a
a.name = 'test'
console.log(a.name)//test
console.log(b.name)//test

ES6有關函數的補充

解構參數

當參數時數組時,函數參數可將數組元素分散爲單個元素(參數用多個值來接收元素)
當參數是對象時,用來接收對象屬性的參數值必須和對象屬性名相同

const obj = {
  x1: "內容1",
  x2: "內容2",
  x3: "內容3",
}

function fun({x1, x2, x3}) {
  return `${x1} ${x2} ${x3}`
}

console.log(fun(obj))//內容1 內容2 內容3

展開運算符

可以使用展開運算符()收集剩下參數(剩餘參數全部放在一個數組裏,而且必須放在最後)

const a = ["x1", "x2", "x3", "x4", "x5"];

function fun(x1, x2, ...arr) {
  console.log(arr)
}

fun('chen','lin',a)//["x1", "x2", "x3", "x4", "x5"]

函數默認參數

function foo(a = 1,b = 2){
  console.log(a,b)
}
foo(5)//5 2

箭頭函數this問題

箭頭函數中本身沒有this,所以的this都是上一作用域的this

call 方法與apply方法

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}

function show(age) {
  console.log("我的年齡" + age)
}

let person = new Person("陳林", 20)
show.call(person, person.age)//我的年齡20

使用方法: 本來person對象中沒有一個方法,但是使用call方法將show函數臨時作爲person對象的方法使用一次.即等價於person.show(person.age) 但是不能這樣寫,因爲該對象中沒有show方法

apply方法與call效果一樣,唯一區別就是,傳入的參數必須爲數組,而call是逐個傳參.

兩個函數的效果總結爲: 讓函數成爲任意指定對象的方法進行調用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章