上篇咱們說到,當咱們須要本身作一些事情的時候,咱們本身基於構造函數,建立的類就是自定義類;javascript
這篇咱們就來講說什麼是構造函數。java
構造函數,字面上的意思理解:數組
- 「構造」:就是經過某種手段或者方法,創造出來(重構出來);
- 「函數」:就是咱們以前學過的函數
這麼一想,構造函數 的意思就是經過某種手段或者方法,創造出來(重構出來)一個函數瀏覽器
那麼怎麼建立呢?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
new 函數()
=> 這種方式就是基於構造函數的方式來執行就像是這樣👇:3d
function Func(name, age) {
this.name = name;
this.age = age;
}
let f1 = new Func('金色小芝麻', 18);
複製代碼
此時:
Func
不在被譽爲普通函數,而是叫作構造函數(也就是咱們所謂的自定義類)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
仍是如下👇題爲例:
function Func(name, age) {
this.name = name;
this.age = age;
}
let f = Func('小芝麻', 18);
let f1 = new Func('小芝麻', 18);
複製代碼
爲了方便理解,直接看圖
仍是以上體爲例:這裏我直接省略全局下的代碼操做過程,直接看函數執行是的私有做用域中的操做過程;
<EC(FUNC),EC(G)>
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
了,防止實例被覆蓋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" 忽略上面的報錯
複製代碼
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(屬性)
TRUE
,若是不是對象的屬性或者不是私有的屬性都返回FALSE
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
複製代碼