function name([param[, param[, ... param]]]) {
statements
}
// name 函數名.
// param 傳遞給函數的參數的名稱,一個函數最多能夠有255個參數.
// statements 組成函數體的聲明語句.
複製代碼
函數聲明語句「被提早」到外部腳本或外部函數做用域的頂部,因此能夠被在它定義以前出現的代碼所調用。javascript
var myFunction = function name([param[, param[, ... param]]]) {
statements
}
// name 函數名,能夠省略。當省略函數名的時候,該函數就成爲了匿名函數。
// param 與 statements的做用和函數聲明中同樣。
複製代碼
函數表達式不會提高,因此不能在定義以前調用。java
([param] [, param]) => { statements }
param => expression
// param 參數名稱. 零參數須要用()表示. 只有一個參數時不須要括號. (例如 foo => 1)
// statements or expression 多個聲明statements須要用大括號括起來,而單個表達式時則不須要。表達式expression也是該函數的隱式返回值。
複製代碼
由於函數是javascript中的特殊對象,因此它們也能夠擁有屬性和方法。程序員
函數的length屬性是隻讀屬性,他表明函數實參的數量。es6
瞭解更多請看這篇文章 juejin.im/post/5e6afa…express
同上編程
方法返回一個字符串,這個字符串和函數聲明語句的語法有關,大多數(非所有)的toString()方法的實現都返回函數的完整源碼。瀏覽器
所謂」構造函數」,就是專門用來生成實例對象的函數。它就是對象的模板,描述實例對象的基本結構。閉包
構造函數就是一個普通的函數app
var Animal = function () {
this.name = 'dog';
};
複製代碼
上面代碼中,Animal就是構造函數。爲了與普通函數區別,構造函數名字的第一個字母一般大寫。ide
構造函數的特色有兩個。
new命令的做用,就是執行構造函數,返回一個實例對象。
var Animal = function () {
this.name = 'dog';
};
var v = new Animal();
v.name // dog
複製代碼
解析:
若是忘了使用new命令,直接調用構造函數時,構造函數就變成了普通函數,並不會生成實例對象。
使用new命令時,它後面的函數依次執行下面的步驟。
解析: 也就是說,構造函數內部,this指的是一個新生成的空對象,全部針對this的操做,都會發生在這個空對象上。構造函數之因此叫「構造函數」,就是說這個函數的目的,就是操做一個空對象(即this對象),將其「構造」爲須要的樣子。
function _new() {
var obj = new Object(); // 1.建立一個空對象,做爲將要返回的對象實例。
var Constructor = [].shift.call(arguments); // 取出構造函數。
obj.__proto__ = Constructor.prototype; // 2.將這個空對象的原型,指向構造函數的prototype屬性。
var ret = Constructor.apply(obj, arguments); // 3.將這個空對象賦值給函數內部的this關鍵字。4.開始執行構造函數內部的代碼。
return typeof ret === 'object' ? ret : obj;
}
複製代碼
JavaScript 的設計是一個簡單的基於對象的範式。一個對象就是一系列屬性的集合,一個屬性包含一個名和一個值。一個屬性的值能夠是函數,這種狀況下屬性也被稱爲方法。除了瀏覽器裏面預約義的那些對象以外,你也能夠定義你本身的對象。
對象基礎知識請看這三篇文章
爲了定義對象類型,爲對象類型建立一個函數以聲明類型的名稱、屬性和方法。例如,你想爲汽車建立一個類型,而且將這類對象稱爲 car ,而且擁有屬性 make, model, 和 year,你能夠建立以下的函數:
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
// 注意經過使用 this 將傳入函數的值賦給對象的屬性。
複製代碼
你能夠經過調用 new 建立任意數量的 car 對象。例如:
var kenscar = new Car("Nissan", "300ZX", 1992);
var vpgscar = new Car("Mazda", "Miata", 1990);
// 這就是上文討論的構造函數的做用
複製代碼
對象也能夠用 Object.create()方法建立。該方法很是有用,由於它容許你爲建立的對象選擇一個原型對象,而不用定義構造函數。
var Animal = {
type: "Invertebrates", // 屬性默認值
displayType : function() { // 用於顯示type屬性的方法
console.log(this.type);
}
}
// 建立一種新的動物——animal1
var animal1 = Object.create(Animal); // Animal 是一個普通對象,不是構造函數,咱們也能創造出新動物。
animal1.displayType(); // Invertebrates
複製代碼
每個javascript對象(null除外)都和另外一個對象相關聯。另外一個對象就是咱們所說的原型,每個對象都從原型繼承屬性。
每一個實例對象( object )都有一個私有屬性(稱之爲 _ _ proto _ _)指向它的構造函數的原型對象(prototype )。該原型對象也有一個本身的原型對象( _ _ proto _ _ ) ,層層向上直到一個對象的原型對象爲 null。根據定義,null 沒有原型,並做爲這個原型鏈中的最後一個環節。
從上文可知,全部的函數會有一個特別的屬性 —— prototype
function wqh(){};
console.log( wqh.prototype );
複製代碼
能夠看到輸出的prototype是一個對象,有兩個屬性constructor和 _ _ proto _ _
讓咱們點開 _ _ proto _ _ 查看更多內容
由上文咱們知道對象的_ _ proto _ _指向他的它的構造函數的原型對象(prototype),查看截圖咱們知道,這裏指向了Object原型
function wqh(){};
wqh.prototype.age = 18
console.log( wqh.prototype );
複製代碼
讓咱們來查看輸出結果
function wqh(){};
wqh.prototype.age = 18;
var good = new wqh();
good.height = 178;
console.log( good.age );
console.log( good );
複製代碼
讓咱們來查看輸出結果
可知,new出來的實例對象的_ _ proto _ _屬性與wqh構造函數的prototype屬性如出一轍。因此實例對象的 _ _ proto _ _ 指向它的構造函數的原型對象(prototype )
這一層一層錯的_ _ proto _ _就是原型鏈。當實例上沒有age屬性時,他會去 _ _ proto _ _原型上查找,若是尚未會繼續往上層查找,這個過程就是原型鏈查找。
根據上文討論,畫圖得
因此當咱們寫構造函數時,若是想寫一些公共的(可繼承的)屬性或方法,能夠寫在prototype原型上。
還有個好處是能夠節省內存;例如,在構造函數上寫了一個方法,若是new了100個實例,那這個方法將會被構造生成100次,若是將這個方法寫在構造函數的原型上,那它只會構造生成一次,new出的實例會在原型鏈上查找這個方法,大大節省內存。
繼承這個概念術語我最先是從java裏接觸到的,但咱們js裏的這個原型鏈是繼承嗎?
引用《你不知道的JavaScript》中的話:繼承意味着複製操做,然而 JavaScript 默認並不會複製對象的屬性,相反,JavaScript 只是在兩個對象之間建立一個關聯,這樣,一個對象就能夠經過委託訪問另外一個對象的屬性和函數,因此與其叫繼承,委託的說法反而更準確些。
但咱們通常仍是會說繼承,由於從理解層面來講意思是同樣的,這樣跟非javascript程序員就能夠正常交流,和同門的js程序員交流必定要講原型鏈和原型鏈查找,這樣更準確。
類的概念:一組具備相同屬性和行爲的對象的抽象。Javascipt語法(es6前)不支持"類"(class)
因此總結來講:js(es6後)中的類仍是原型和基於原型鏈的‘繼承’來寫的構造函數。只是爲了語法簡潔,通用,便於理解(理解類比理解原型鏈簡單多了)封裝的語法。
// 定義構造函數
// 這裏遵循一個常見的編程約定:從某種意義上講,定義構造函數既是定義類,而且類名首字母要大寫。
function People(name, age) {
this.name = name;
this.age = age;
};
People.prototype.showName = function () {
console.log(this.name);
};
// 使用構造函數
var p1 = new People('wqh', 18);
p1.showName();
var p2 = new People('w', 19);
var p3 = new People('q', 20);
console.log(p1, p2, p3);
複製代碼
查看輸出結果咱們發現:
function People(name, age) {
this.name = name;
this.age = age;
};
People.prototype = {
showName: function () {
console.log(this.name);
}
}
// 使用構造函數
var p1 = new People('wqh', 18);
p1.showName();
var p2 = new People('w', 19);
var p3 = new People('q', 20);
console.log(p1, p2, p3);
複製代碼
咱們發現當prototype重定義爲一個對象時,這個新定義的原型對象不含有constructor屬性,所以類的實例也不含有constructor屬性。
顯式給原型添加一個構造函數
function People(name, age) {
this.name = name;
this.age = age;
};
People.prototype = {
constructor: People,
showName: function () {
console.log(this.name);
}
}
// 使用構造函數
var p1 = new People('wqh', 18);
p1.showName();
var p2 = new People('w', 19);
var p3 = new People('q', 20);
console.log(p1, p2, p3);
複製代碼
class People {
constructor (name, age) {
this.name = name;
this.age = age;
}
showName () {
console.log(this.name);
}
};
// 使用構造函數
var p1 = new People('wqh', 18);
p1.showName();
var p2 = new People('w', 19);
var p3 = new People('q', 20);
console.log(p1, p2, p3);
複製代碼
class People {
constructor() {
// ...
}
showName() {
// ...
}
}
// 等同於
People.prototype = {
constructor() {},
showName() {},
};
// 在類的實例上面調用方法,其實就是調用原型上的方法。
// p2.constructor === People.prototype.constructor // true
複製代碼
class People {
hello = 'hello';
world = 'world';
constructor (name, age) {
this.name = name;
this.age = age;
}
showName () {
console.log(this.name);
}
};
// 使用構造函數
var p1 = new People('wqh', 18);
p1.showName();
var p2 = new People('w', 19);
var p3 = new People('q', 20);
console.log(p1, p2, p3);
複製代碼
常常據說一句話 ——— ‘js一切皆對象’(排除基本類型)。咱們一開始就說函數是特殊的對象,因此從函數到類其實都是對象,類也是函數,由於類的數據類型就是函數。因此在js世界裏,一切皆對象是真實存在的。
因此下次打算梳理 ——— ‘js面向對象編程’。