最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript中函數(shù)的四種調(diào)用方式總結(jié)

 更新時(shí)間:2023年10月30日 14:02:28   作者:Knockkk  
這篇文章主要為大家詳細(xì)介紹了JavaScript中函數(shù)的四種調(diào)用方式,文中的示例代碼講解詳細(xì),對我們深入掌握J(rèn)avaScript有一定的幫助,需要的可以參考下

在《JavaScript忍者秘籍》書中提到,我們有四種不同的方式進(jìn)行函數(shù)調(diào)用:

  • 作為一個(gè)函數(shù)進(jìn)行調(diào)用,這是最簡單的形式。
  • 作為一個(gè)方法進(jìn)行調(diào)用,在對象上進(jìn)行調(diào)用,支持面向?qū)ο缶幊獭?/li>
  • 作為構(gòu)造器進(jìn)行調(diào)用,創(chuàng)建一個(gè)新對象。
  • 通過apply() 或call() 方法進(jìn)行調(diào)用。

作為一個(gè)曾經(jīng)的小鎮(zhèn)做題家,一些關(guān)鍵詞自然而然就冒出來了,this、閉包、原型......這些概念都不陌生,但總感覺有些散亂。我發(fā)現(xiàn),順著書中這幾種函數(shù)調(diào)用方式,層層遞進(jìn),倒是可以幫助更好的理解和串聯(lián)這些知識點(diǎn)。

1. 作為函數(shù)調(diào)用

這里指的就是最簡單直接的調(diào)用方式:

function sum(a, b) {
  return a + b;
}

sum(1, 2)

函數(shù)封裝了一段邏輯,給一個(gè)輸入,得到一個(gè)輸出。函數(shù)最大的好處是它是可以復(fù)用的,而不是在每個(gè)需要的地方都寫一段重復(fù)的代碼。用好函數(shù)往往可以讓代碼變得更清晰。

使用函數(shù)時(shí)必然要關(guān)注它的作用域。簡單來說,函數(shù)內(nèi)是局部變量,只能在這個(gè)函數(shù)中訪問,而全局變量可以在程序的任何代碼中訪問。但在JS中比這復(fù)雜,因?yàn)楹瘮?shù)是可以嵌套的,也可以作為參數(shù)傳遞。比如下面的例子:

function outer() {
  let value = 0;

  function inner(n) {
    value += n;
    return value;
  }

  return inner;
}

const fn = outer();
fn(1); // 1
fn(2); // 3

此時(shí)inner()作為內(nèi)部函數(shù)是可以訪問到外部函數(shù)outer()內(nèi)定義的局部變量的,即使它們是兩個(gè)不同的函數(shù)。另外,正常來說,一個(gè)函數(shù)執(zhí)行完成后,所有函數(shù)內(nèi)定義的局部變量都會被回收,但這里不同:outer()執(zhí)行完成了,返回了一個(gè)函數(shù),而這個(gè)函數(shù)內(nèi)仍然能訪問value這個(gè)局部變量(value并沒有被回收)——即“閉包”。因?yàn)镴S支持更靈活的函數(shù)使用,所以也就引出了這些概念,這在Go語言中也是一摸一樣的。

2. 作為方法進(jìn)行調(diào)用

即函數(shù)作為對象的一個(gè)屬性,如下所示:

const dog = {
  name: "wangcai",
  age: 1,
  sayHi: function () {
    console.log("Hello~");
  },
};

dog.sayHi(); // Hello~

JS中可以用對象表示一些屬性的集合,比如一只狗有名字和年齡。當(dāng)然這個(gè)對象也可以有方法,比如這只狗會打招呼。如果想在打招呼的時(shí)候報(bào)出自己的名字怎么做呢?當(dāng)然我們可以直接用對象名訪問,即dog.name,但還有一種更友好的方式——this(在Java中,關(guān)鍵字“this”表示當(dāng)前對象的引用,而在JS中,“this”是支持面向?qū)ο缶幋a的主要手段之一)。

const dog = {
  name: "wangcai",
  age: 1,
  sayHi: function () {
    console.log("Hello,my name is ", this.name);
  },
};

dog.sayHi(); // Hello,my name is  wangcai

這樣就清晰多了,不用管變量名的dog是哪個(gè)dog,this就是本狗了。那普通函數(shù)不在對象中,它調(diào)用時(shí)的this是什么呢?

function getThis() {
  return this;
}

getThis() === window // true

在瀏覽器中,答案就是全局環(huán)境window?;氐角懊鎑og的例子,下面代碼中this又是什么呢?是本狗嗎?答案我們在后面揭曉。

const dog = {
  name: "wangcai",
  age: 1,
  sayHi: function () {
    console.log("Hello,my name is ", this.name);
  },
};

const sayHi = dog.sayHi;

sayHi();

3. 作為構(gòu)造器進(jìn)行調(diào)用

如果我有很多條狗,那么用對象來一個(gè)個(gè)聲明就略顯笨拙了。你可能會寫一個(gè)createDog的函數(shù),來按模式批量生產(chǎn)dog。

function createDog(name, age) {
  return {
    name,
    age,
    sayHi: function () {
      console.log("Hello,my name is ", this.name);
    },
  };
}
const dog = createDog("wangcai", 1);

但同樣,有一種更“面向?qū)ο?rdquo;,更友好的方式——構(gòu)造函數(shù)(constructor,ES6的“class”就是構(gòu)造函數(shù)的語法糖) 。

function Dog(name, age) {
  this.name = name;
  this.age = age;
  this.sayHi = function () {
    console.log("Hello,my name is ", this.name);
  };
}

const dog = new Dog("wangcai", 1);

構(gòu)造函數(shù)需要搭配“new”關(guān)鍵字使用,它將自動(dòng)返回一個(gè)新的對象。另外,構(gòu)造函數(shù)一般用大寫開頭,同時(shí)它應(yīng)該是一個(gè)名詞,而不是動(dòng)詞。

構(gòu)造函數(shù)只是一個(gè)約定,語言本身并沒有限制你如何使用它。你可以直接執(zhí)行Dog("wangcai",i),但這樣沒有什么意義,還會產(chǎn)生一些意外的全局變量。

我們可以結(jié)合前一節(jié)的“對象方法”來理解構(gòu)造器的調(diào)用過程:

  • 創(chuàng)建一個(gè)空對象
  • 在該對象上執(zhí)行這個(gè)函數(shù)(函數(shù)調(diào)用時(shí)的this指向這個(gè)對象)
  • 最后將這個(gè)對象返回,如果沒有顯式的返回值(還差了一個(gè)步驟,暫時(shí)沒涉及,后面再說)

JS中幾乎所有對象都有自己的構(gòu)造函數(shù),對于使用字面量語法聲明的對象,它的構(gòu)造函數(shù)就是Object。

const dog = { name: "wangcai" };

JS中有三種方式創(chuàng)建一個(gè)對象。

  • 字面量語法,如const obj = { value: 100 }
  • Object.create()
  • 使用new調(diào)用構(gòu)造函數(shù)初始化一個(gè)對象

只有通過Object.create(null)創(chuàng)建的對象是沒有構(gòu)造函數(shù)的,也沒有“原型”。

對象與構(gòu)造函數(shù)之間通過原型進(jìn)行關(guān)聯(lián)。還是以我們的dog為例:

const dog = new Dog("wangcai", 1);

Object.getPrototypeOf(dog) === Dog.prototype // true

“當(dāng)構(gòu)造函數(shù)搭配new使用時(shí),該函數(shù)的prototype數(shù)據(jù)屬性將用作新對象的原型。默認(rèn)情況下,函數(shù)的prototype是一個(gè)普通的對象。這個(gè)對象具有一個(gè)屬性:constructor,它是對這個(gè)函數(shù)本身的一個(gè)引用。 constructor 屬性是可編輯、可配置但不可枚舉的”。所以,我們通過Object.getPrototypeOf(dog).constructor可以直接獲取到對象的構(gòu)造函數(shù)。下面就是瀏覽器控制臺打印出的dog對象:

那對象的原型又是什么呢?它是JS中一種獨(dú)特的機(jī)制,它的特點(diǎn)如下:

  • 每個(gè)對象都有一個(gè)私有屬性指向另一個(gè)名為原型(prototype)的對象。當(dāng)訪問一個(gè)對象屬性時(shí),如果屬性不存在,就會繼續(xù)查找這個(gè)對象的原型屬性。
  • 原型對象也有一個(gè)自己的原型,層層向上直到一個(gè)對象的原型為null。Object.prototype的原型始終為null且不可更改。

所以,一個(gè)對象不僅有實(shí)例屬性,還有原型屬性,它們都可以被訪問到,只是原型屬性是不可枚舉的。

const o = { value: 100};

Object.keys(o); // ['value']

o.valueOf(); // valueOf是構(gòu)造函數(shù)Object的prototype上定義的方法,可以正常訪問

再回顧new進(jìn)行初始化的過程,是缺了什么步驟呢?就是將對象的原型指向構(gòu)造函數(shù)的prototype。我們可以按這個(gè)規(guī)則模擬一個(gè)new函數(shù)。

function Dog(name, age) {
  this.name = name;
  this.age = age;
}

Dog.prototype.sayHi = function () {
  console.log("Hello,my name is ", this.name);
};

function myNew(fn, ...args) {
  const obj = Object.create(fn.prototype); // 創(chuàng)建一個(gè)對象,將對象的原型指向fn.prototype
  const res = fn.apply(obj, args);

  return res ?? obj;
}

const dog = myNew(Dog, "wangcai", 1);

dog.sayHi(); // Hello,my name is wangcai

Object.getPrototypeOf(dog) === Dog.prototype; // true

大家細(xì)心會發(fā)現(xiàn),這個(gè)例子中將sayHi函數(shù)放在了Dog的prototype對象上,而不像之前在函數(shù)內(nèi)通過this.sayHi聲明。兩種方式new出來的對象都是能調(diào)用sayHi()的,唯一的區(qū)別是:一個(gè)是通過對象的實(shí)例屬性訪問,而另一個(gè)是通過原型屬性訪問。后者看起來是這樣的:

原型屬性具有一些優(yōu)點(diǎn),比如在這個(gè)例子中,每個(gè)new出來的對象訪問的都是同一個(gè)sayHi函數(shù)(定義在Dog的prototype對象上),而不是重新拷貝,這樣節(jié)省內(nèi)存。同時(shí),sayHi函數(shù)只需修改一次,就能應(yīng)用到所有實(shí)例化的對象中。

Dog.prototype.sayHi = function () {
  console.log("Good morning~");
};

dog.sayHi(); // Good morning~

原型鏈的特性還能支持我們實(shí)現(xiàn)對象的“繼承”。首先我們用class語法試下繼承的效果。

// 基類
class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} is eating`);
  }
}

//派生類
class Dog extends Animal {
  constructor(name, age) {
    super(name);
    this.age = age;
  }

  sayHi() {
    console.log(`Hello, my name is ${this.name}, I am ${this.age} years old`);
  }
}

const dog = new Dog("wangcai", 1);

dog.sayHi(); // Hello, my name is wangcai, I am 1 years old
dog.eat(); // wangcai is eating

分析一下,agename都在dog對象的實(shí)例屬性上,而sayHi() 函數(shù)在dog對象的原型上,即Dog.prototype。再往上一層,Dog.prototype這個(gè)對象的原型是Animal.prototype,Animal.prototype上具有eat()方法。那再往上一層呢?Animal.prototype這個(gè)對象的原型是Object,再往上就到原型鏈頂端null了。

接下來相信大家也有概念怎么手動(dòng)實(shí)現(xiàn)繼承了。下面是一個(gè)示例:

function Animal(name) {
  this.name = name;
}

Animal.prototype.eat = function () {
  console.log(`${this.name} is eating`);
};

function Dog(name, age) {
  this.age = age;
  Animal.call(this, name); // 將Animal的實(shí)例屬性放到Dog對象上

  Object.setPrototypeOf(Dog.prototype, Animal.prototype); // 設(shè)置原型鏈
}

Dog.prototype.sayHi = function () {
  console.log(`Hello, my name is ${this.name}, I am ${this.age} years old`);
};

const dog = new Dog("wangcai", 1);

dog.sayHi();
dog.eat();

4. 通過apply()或call()方法進(jìn)行調(diào)用

const dog = {
  name: "wangcai",
  age: 1,
  sayHi: function () {
    console.log("Hello,my name is ", this.name);
  },
};

const sayHi = dog.sayHi;

sayHi();

再回顧前面的例子,揭曉答案,結(jié)果是Hello,my name is undifined,顯然不是本狗了。

為什么會這樣呢?因?yàn)镴S中函數(shù)的“this”是由調(diào)用點(diǎn)決定的(在運(yùn)行時(shí)確定) ,而不是在函數(shù)聲明處決定。這就讓人很困擾了,本狗的名字都對不上了,這樣真的好嗎?其實(shí)這樣是為了提供更大的靈活性——支持動(dòng)態(tài)上下文。

function sayHi() {
  console.log("Hello,my name is ", this.name);
}

const dog = {
  name: "wangcai",
  sayHi,
};

const cat = {
  name: "miaomiao",
  sayHi,
};

dog.sayHi(); // Hello,my name is wangcai
cat.sayHi(); // Hello,my name is miaomiao

通過允許this由調(diào)用點(diǎn)動(dòng)態(tài)確定,可以讓同一個(gè)函數(shù)在不同的對象上使用。另外,JS也提供了顯式指定函數(shù)執(zhí)行時(shí)的this為某個(gè)對象的方法,即apply()call()。

const dog = {
  name: "wangcai",
  age: 1,
  sayHi: function () {
    console.log("Hello,my name is ", this.name);
  },
};

const sayHi = dog.sayHi;

sayHi.apply(dog); // Hello,my name is  wangcai
sayHi.call(dog); // Hello,my name is  wangcai

很開心,本狗又回來了。apply()call()只有函數(shù)傳參上的差異,在使用時(shí)看哪個(gè)方便用哪個(gè)就行。

總結(jié)

通過體驗(yàn)函數(shù)這四種不同的調(diào)用方式,我們逐漸接觸了JS中一些底層的知識點(diǎn):作為函數(shù)調(diào)用時(shí)的閉包,作為方法調(diào)用時(shí)的this指向,作為構(gòu)造器調(diào)用時(shí)的原型。JS中的函數(shù)非常靈活,也很強(qiáng)大,并且與對象有著密切的聯(lián)系。

以上就是JavaScript中函數(shù)的四種調(diào)用方式總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript函數(shù)調(diào)用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

黄骅市| 宜春市| 溆浦县| 拜城县| 枝江市| 于田县| 白河县| 会理县| 长汀县| 永顺县| 舟山市| 如皋市| 普陀区| 武山县| 建宁县| 修武县| 昭通市| 土默特左旗| 松原市| 榆树市| 金堂县| 剑川县| 白银市| 丰顺县| 大埔区| 涞源县| 高台县| 江口县| 九龙县| 嘉鱼县| 修文县| 孟津县| 宁远县| 台山市| 独山县| 石棉县| 高唐县| 大竹县| 绥中县| 吐鲁番市| 阆中市|