構造函數建立自定義類

上篇咱們說到,當咱們須要本身作一些事情的時候,咱們本身基於構造函數,建立的類就是自定義類;javascript

這篇咱們就來講說什麼是構造函數。java

思惟導圖

1、構造函數語法

構造函數,字面上的意思理解:數組

  • 「構造」:就是經過某種手段或者方法,創造出來(重構出來);
  • 「函數」:就是咱們以前學過的函數

這麼一想,構造函數 的意思就是經過某種手段或者方法,創造出來(重構出來)一個函數瀏覽器

那麼怎麼建立呢?bash

首先咱們來看一個👇普通函數函數

function Func(name, age) {
    this.name = name; 
    this.age = age;
}
let f1 = Func('金色小芝麻', 18);//=> 把Func函數執行(當作普通函數執行)
//=>方法中的THIS:window
console.log(f1); //=>undefined 由於沒有返回值
console.log(window.name, window.age); //=>'金色小芝麻' 18
複製代碼

這是咱們的普通函數執行,在上面的代碼的基礎上咱們加個new,一切就都變了ui

function Func(name, age) {
    this.name = name; 
    this.age = age;
}
let f1 = new Func('金色小芝麻', 18);
console.log(f1); //=> {name: "金色小芝麻", age: 18}
console.log(window.name, window.age); //=> window.name是空 window.age是undefined
複製代碼

此時咱們很明顯發現不同了,由window.name不在是‘金色小芝麻’, window.age不在是‘18’,咱們能夠判定,函數體中的this毫不在是window了,並且在沒有return的狀況下f1不在是undefined了;this

這種在函數執行前加一個new的方式,就是咱們的構造函數執行;spa


  • 在ES3語法中:
    • new 函數() => 這種方式就是基於構造函數的方式來執行
    • 約定的語法規範:類名的第一個字母通常都是大寫的

就像是這樣👇:3d

function Func(name, age) {
    this.name = name; 
    this.age = age;
}
let f1 = new Func('金色小芝麻', 18);
複製代碼

此時:

    1. Func不在被譽爲普通函數,而是叫作構造函數(也就是咱們所謂的自定義類)
    1. 返回的結果也再也不基於RETURN來判斷返回值,返回的結果是當前類的一個實例

想建立自定義類和建立類的實例,只須要在執行的時候 不在 "`函數()`" 普通函數執行; 而是 "`new 函數()`" 執行,也就是構造函數執行,這樣方法被稱爲類,返回結果被稱爲類的實例;

構造函數語法與普通函數語法的區別

function Fn() {
	this.x = 100;
	this.y = 200;
}
let f1 = new Fn();
console.log(f1);
let f2 = new Fn;
console.log(f2); 
複製代碼
  • 普通函數
    • Fn 是函數自己(不是執行)
    • Fn() 函數執行
  • 構造函數
    • new Fn(); 構造函數執行 =>有參數new
    • new Fn; 構造函數執行(這種方式不能給函數傳遞實參了) =>無參數new

2、構造函數執行與普通函數執行的區別

仍是如下👇題爲例:

function Func(name, age) {
    this.name = name; 
    this.age = age;
}
let f = Func('小芝麻', 18);
let f1 = new Func('小芝麻', 18);
複製代碼

爲了方便理解,直接看圖

一、普通函數執行時

仍是以上體爲例:這裏我直接省略全局下的代碼操做過程,直接看函數執行是的私有做用域中的操做過程;

  • 第一步:初始化做用域鏈:<EC(FUNC),EC(G)>
  • 第二步:初始化THIS:window
  • 第三步:形參賦值:name = '小芝麻'age = 18
  • ......省略不重要的步驟。
  • 第四步:變量提高:無
  • 第五步:代碼執行:
    • this.name = name; //=> window.name = '小芝麻'
    • this.age = age;//=> window.age= 18

沒有返回值,執行完成出棧銷燬:f = undefined

二、構造函數執行時

構造函數擁有普通函數執行的特徵,也有本身單獨的一些操做

  • 第一步:初始化做用域鏈:<EC(FUNC),EC(G)>
  • 第二步:形參賦值:name = '小芝麻'age = 18
  • 第三步:變量提高:無

以上三步說明了構造函數具有普通函數的一面;

構造函數執行時瀏覽器多作的一些事情👇:

  • 第四步:在當前上下文中,建立一個對象(這個對象就是當前類的實例)=> AAAFFF111(咱們假設的空間地址)

  • 第五步:讓當前上下文中的THIS指向新建立的實例對象:this :AAAFFF111

  • 第六步:代碼執行

    • this.name = name; //=> 把私有變量的name值,賦給新建立實例對象的私有屬性
    • this.age = age;//=> 把私有變量的age值,賦給新建立實例對象的私有屬性
  • 第七步:代碼執行完,若是咱們沒有設置RETURN,瀏覽器默認會把新建立的實例對象返回

相信看到這裏你已經明白其中的道理了,咱們在簡單總結下:

function Func(name, age) {
	/* * 代碼執行以前,建立一個實例對象(堆) * 讓THIS指向實例對象 */
	this.name = name; //=>this.xxx=xxx 都是在給實例對象設置私有的屬性和方法
	this.age = age;
	/* * 若是函數沒有return,默認會把建立的實例對象返回 */
}
let f1 = new Func('小芝麻', 18);
let f2 = new Func('金色', 10);
console.log(f1);//=> {name: "小芝麻", age: 18}
console.log(f2);//=> {name: "金色", age: 10}
console.log(f1 === f2); //=>FALSE 每次都是建立一個新的實例,每個實例和其餘實例都是一個單獨的對象(個體),互相之間不衝突
複製代碼

總結以下:

三、構造函數執行時:實例的相關問題

  • 一、構造函數執行,因爲具有普通函數特徵,因此在私有上下文中可能會出現一些私有變量,可是這些私有變量和實例沒有必然的聯繫,私有上下文中的THIS纔是實例,因此只有寫THIS.XXX=XXX的操做,纔是給實例設置私有屬性;
    • 實例的私有屬性和上下文中的私有變量不是一個東西
  • 二、當前類的每個實例都是單獨的一個對象,實例和實例之間是獨立的
  • 三、在構造函數的函數體中,基於 THIS.XXX=XXX 給實例設置的屬性和方法都是本身私有的,和其它實例中的屬性和方法不衝突

四、構造函數執行時:return的相關問題

function Fn() {
	this.x = 100;
	this.y = 200;
	return 1;
}
let f1 = new Fn;
console.log(f1); //=>{x: 100, y: 200} 仍然返回當前實例

function Fn() {
	this.x = 100;
	this.y = 200;
	return {
		name: 'xxx'
	};
}
let f1 = new Fn;
console.log(f1); //=>{name:'xxx'} 再也不是Fn類的實例
複製代碼

總結:

  • 一、沒有return,默認返回當前類的實例對象(對象數據類型)
  • 二、有return,而且返回基本類型值,最後的返回結果仍是類的實例,不會有影響
  • 三、若是返回的是一個引用數據類型,則會把默認返回的實例給覆蓋掉,這樣咱們接收結果就不在是當前類的實例了,而是本身返回的值 => 真實項目中,若是想建立類的實例,則建議你們不要在手動寫return了,防止實例被覆蓋

3、一道例題

function Fn(x) {
	let y = 20;
	this.total = x + y;
	this.say = function () {
		console.log(`${x} + ${y} = ${this.total}`);
	};
}
let f1 = Fn(10); //=>f1=undefined
let f2 = new Fn(10); //=>f2實例對象
let f3 = new Fn(20); //=>f3實例對象

console.log(f2.total); //=>30
console.log(f2.x); //=>undefined
console.log(f3.y); //=>undefined
console.log(f1 === f2); //=>FALSE
console.log(f2 === f3); //=>FALSE
f2.say(); //=>"10+20=30"
console.log(f2.say === f3.say); //=>FALSE 都是當前實例的私有屬性和方法(全部出如今構造函數的函數體中的 this.xxx=xxx 操做,都是設置私有的屬性和方法)
console.log(f1.total); //=>Uncaught TypeError: Cannot read property 'total' of undefined f1不是對象,只有對象才能操做鍵值對
console.log(window.total); //=>30 忽略上面的報錯後輸出結果
window.say(); //=>"10+20=30" 忽略上面的報錯
複製代碼

4、檢測實例的幾種方法

一、instanceof:檢測當前實例是否屬於這個類

instanceof: 檢測當前實例是否屬於這個類(或者檢測當前值是否爲某個類的實例)

  • 語法: 值 instanceof

  • 返回值: 是它的實例返回TRUE,反之返回FALSE

    (當前類的原型只要出如今了實例的原型鏈上就返回true,原型鏈概念咱們下一篇講解)

    function Fn() {}
    let f1 = new Fn;
    console.log(f1 instanceof Fn); //=>TRUE
    console.log(f1 instanceof Array); //=>FALSE
    複製代碼
  • 侷限:instanceof不適用於基本數據類型檢測,要求檢測的實例必須是對象數據類型的

    console.log(100 instanceof Number); //=>FALSE
    複製代碼
  • 應用場景:區分對象中的特殊數據格式,例如數組或者正則

    let arr = [10, 20];
    let reg = /^$/;
    // console.log(typeof arr); //=>"object" typeof不能具體檢測對象類型的細分
    // console.log(typeof reg); //=>"object"
    console.log(arr instanceof Array); //=>TRUE
    console.log(arr instanceof RegExp); //=>FALSE
    複製代碼

二、hasOwnProperty:檢測當前屬性是否爲實例私有屬性

hasOwnProperty:檢測當前的某一個屬性是否爲實例(或者對象)的私有屬性

  • 語法:對象.hasOwnProperty(屬性)
  • 返回值:是私有的屬性返回TRUE,若是不是對象的屬性或者不是私有的屬性都返回FALSE

三、in檢測當前屬性是否爲對象的屬性

in:檢測當前屬性是否爲對象的屬性

  • 語法:屬性 in 對象

  • 返回值:只要是對象的屬性(不論是公有仍是私有的屬性)結果都是TRUE

    function Fn() {
    	// 構造函數體中出現的 this.xxx = xxx 都是給當前實例設置的私有屬性
    	this.x = 100;
    	this.y = 200;
    	this.say = function () {
    		console.log(x + y);
    	};
    }
    let f1 = new Fn;
    let f2 = new Fn;
    console.log(f1.say === f2.say); //=>false
    
    console.log(f1.hasOwnProperty('say')); //=>true
    console.log(f1.hasOwnProperty('name')); //=>false 由於你連這個屬性都沒有
    console.log(f1.toString()); //=>"[object Object]" toString必定是f1對象的屬性,不然f1也不可能調用這個方法
    console.log(f1.hasOwnProperty('toString')); //=>false toString不是它的私有屬性,是他的公有屬性
    
    console.log('say' in f1); //=>true
    console.log('toString' in f1); //=>true
    console.log('name' in f1); //=>false
    複製代碼

利用上面兩個方法咱們能夠本身寫一個檢測某一個屬性是否爲當前對象的公共屬性的方法

需求:檢測某一個屬性是否爲當前對象的公共屬性(是他的屬性,可是還不是私有的)

function Fn() {
    	// 構造函數體中出現的 this.xxx = xxx 都是給當前實例設置的私有屬性
    	this.x = 100;
    	this.y = 200;
    	this.say = function () {
    		console.log(x + y);
    	};
    }
    let f1 = new Fn;
    let f2 = new Fn;
// 檢測ATTR是否爲OBJ的公有屬性
function myHasPublicProperty(attr, obj) {
	// 1.須要是它的屬性 =>IN檢測爲TRUE
	// 2.不是它的私有屬性 =>HASOWNPROPERTY檢測爲FALSE
	// return ((attr in obj) === true) && (obj.hasOwnProperty(attr) === false);或者
	return (attr in obj) && !obj.hasOwnProperty(attr);
}
console.log(myHasPublicProperty('say', f1)); //=>false
console.log(myHasPublicProperty('name', f1)); //=>false
console.log(myHasPublicProperty('toString', f1)); //=>true
複製代碼
相關文章
相關標籤/搜索