JS學習筆記 對象、包裝類、原型、原型鏈、命名空間、對象枚舉
文章目錄
對象
對象的創建
對象直接量/字面量
構造函數(重點)
系統自帶構造函數
自定義構造函數
Object.create
對象的增刪改查
包裝類
原型
call、apply
原型鏈
原型鏈上屬性的增刪改查
絕大多數對象最終都會繼承自Object.prototype
對象繼承史
傳統模式
借用構造函數
共享原型
聖杯模式
命名空間
對象枚舉
對象
對象是JavaScript的一個基本數據類型,是一種複合值,它將很多值(原始值或者其他對象)聚合在一起,可通過名字訪問這些值。即屬性的無序集合。
對象的創建
對象直接量/字面量
var obj = {
name : "守敬",
age : "18"
}
console.log(obj.name);//守敬
1
2
3
4
5
構造函數(重點)
系統自帶構造函數
var obj = new object();
obj.name = "守敬";
console.log(obj.name);//守敬
1
2
3
自定義構造函數
為了和普通函數區分,構造函數採用大駝峰式命名法。
function Car(carName, color) {
this.name = carName;
this.color = color;
}
var car = new Car("BMW", "red");
console.log(car.name);//BMW
console.log(car.color);//red
1
2
3
4
5
6
7
自定義構造函數內部原理
在函數體最前面隱式的加上this{}
執行this.xxx = xxx;
隱式的返回this(return)
原理解釋:
注意隱士創建的this對象中有個名為__proto__的屬性,其屬性值為該構造函數繼承的原型prototype。
function Car(carName, color) {
//隱式創建this對象
//var this = {
// color : "",
// name : "",
// health : "",
// run = function (){
// this.health --;
// }
// };
this. color : color,
this.name : carName,
this.health : 100,
this.run = function () {
//對象方法
this.health --;
}
//return this;返回this對象
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Object.create
此方法創建一個繼承該原型的實例對象
留坑待補
對象的增刪改查
增: 所謂增添一個對象的屬性,就是直接對該屬性進行賦值操作即可,這就相當於為該對象添加了一個新屬性,而列印未添加的屬性,瀏覽器不會報錯,而是會列印出undefined
var obj = {};
console.log(obj.name); //undefined (不會報錯)
obj.name = "shoujing";
console.log(obj.name); // shoujing
1
2
3
4
刪:我們通過delete操作符來刪除一個對象的屬性
var obj = {
name : "shoujing"
}
console.log(obj.name); //shoujing
delete obj.name;
console.log(obj.name); //undefined
1
2
3
4
5
6
改: 修改一個對象的屬性是最簡單的了,直接通過賦值操作賦予其其他的值即可
var obj = {
name: "shoujing"
}
console.log(obj.name); //shoujing
obj.name = "obj";
console.log(obj.name); // obj
1
2
3
4
5
6
查:查詢一個對象的屬性值有兩種方法
var obj = {
name: "shoujing"
};
// 第一種方法
console.log(obj["name"]); //shoujing
// 第二種方法
console.log(obj.name); //shoujing
//p.s.最本質的是第一種方法,因為在使用第二種方法時,後台自動將其轉換為第一種字元串的形式來查詢
1
2
3
4
5
6
7
8
p.s.以上的增、刪、改三種操作都只是針對當前對象的屬性進行操作,而不會影響到當前對象的原型的屬性。而查詢是先看看當前對象本身是否設置了該屬性,如果當前對象未設置該屬性,則再看該對象的原型中是否設置了該屬性,若兩者都沒有,則返回undefined
包裝類
五個原始值:number, string , boolean, undefined, null.其中number, string, boolean是分別擁有自己的包裝類,而undefined和null是沒有自己的包裝類的
原始值不是對象,無法擁有自己的屬性,但因為的包裝類的存在,原始值就好似可以擁有自己的屬性了,但其擁有的屬性又有點特殊之處,如下用string來舉例:
引例:
// str是string類型的,非對象,不能擁有屬性,為什麼能列印出 str.length?
var str = "abcd";
console.log(str.length); //4
1
2
3
解釋:
// 因為每次執行完一條完整js語句後該類型對象的包裝類就會將該語句包裝,所以也就不會導致報錯了
var str = "abcd";
// var str = new String("abcd");
console.log(str.length); //4
str.len = 4;
consloe.log(str.len);//undefinend
//length較為特殊,如果是自定義的len屬性,則系統會進行如下過程:
//(new string("abcd")).len = 4; delete
//因此最後列印就是undefinde
1
2
3
4
5
6
7
8
9
舉例
var str = "abcd";
str.lenth = 2;
console.log(str); // ab or abcd ? answer is abcd
console.log(str.length); // 2 or 4 ? answer is 4
1
2
3
4
原型
原型的定義: 原型是function對象的一個屬性,它定義了構造函數製造出的對象的公共祖先。通過該構造函數產生的對象,可以繼承該原型的屬性和方法。原型也是對象。
利用原型特點和概念,可以提取共有屬性。將一類對象的共有屬性提取出來,放到該類對象的原型中,從而不需要每次用new操作符時都重新定義一遍該共有屬性。
如下,定義一個Person構造函數,而屬於Person多構造對象共有的屬性方法,則定義到Person的原型中
Person.prototype = {
eat : function (food) {
console.log("I have eated " + food);
},
sleep : function () {
console.log("I am sleeping");
}
}
// 人的構造函數
function Person (name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person("shoujing", 18);
console.log(person1.name); //shoujing
person1.eat("apple"); //I have eated apple
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如何查看原型:
之前是不允許我們查看構造函數的原型的,但後來提供了一個可查看構造函數原型的介面:隱士屬性__proto__(其實我們能夠訪問原型的屬性,或者說繼承原型,靠的就是__proto__屬性連接著構造函數和原型,可以說沒有__proto__屬性的存在,就無法實現原型的繼承)
首先我們先說明一下__proto__這個介面是存放到哪裡的看過以上對象創建過程的都應該知道在用new創建一個對象時,內部會隱式自動創建一個this的對象,進過一系列處理後再隱士將this對象返回。而__proto__就在隱士創建的this對象中,如下代碼:
// 原型
Person.prototype = {
say: function () {
console.log("I am saying ");
},
play: function () {
console.log("I am playing");
}
}
// 構造函數
function Person (name) {
// var this = Object.create(Person.prototype);
// p.s.在隱士創建的this對象中存在一個屬性,即__proto__,該屬性存儲了Person.prototype
this.name = name;
// return this;
}
// 對象的創建
var person1 = new Person("lyl");
// 列印原型
console.log(person1.__proto__);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 如何查看對象的構造函數,我們通過屬性constructor來查看:contructor屬性位於構造函數的原型中,其中存儲的是構造函數信息,所以在不知道原型的情況下,由原型繼承原理,我們可以用實例對象來直接訪問constructor,即獲取創建該實例的構造函數。
1
function Person () {
this.name = "myName";
this.age = 18;
}
var person = new Person();
console.log(person.constructor); // function Person(){...}
1
2
3
4
5
6
call、apply
call和apply的作用是改變構造函數中this的指向,二者唯一的區別在一參數傳遞的方式不同,如:
function Person (name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student (name, age, sex, grade){
Person.call(this, name, age, sex)//call傳參
Person.apply(this, [name, age, sex])//apply傳參使用數組
this.grade = grade;
}
1
2
3
4
5
6
7
8
9
10
11
原型鏈
定義:顧名思義,原型鏈就是將一個個原型串連起來,形成一條原型繼承的鏈子。
原型鏈的構成
如下代碼例子, Son繼承Father, Father繼承Grand, 而Grand沒有自定義原型,所以默認為原型鏈的最頂端new Object();為什麼Object為最頂端,因為Object.prototype為null,為null是沒有原型的。
//原型鏈 Son --> new Father() --> new Grand() --> new Object();
function Grand () {
}
Father.prototype = new Grand();
function Father () {
}
Son.prototype = new Father();
function Son () {
}
var son = new Son();
console.log(son);
1
2
3
4
5
6
7
8
9
10
11
原型鏈上屬性的增刪改查
以上述原型鏈為例說明:Son --> new Father() --> new Grand() --> new Object()
增
為son實例對象添加屬性,總是添加為其自己本身的屬性,為對原型和原型鏈是沒有影響的。(再具體說即對和其相同構造函數構造的實例對象無法造成影響,以下說法同此)
刪
使用delete操作符只能刪除son實例對象自己本身的屬性,而無法刪除由原型繼承而來的屬性.
改
若修改的屬性為繼承自原型的,且值類型為原始值,則僅僅修改的是該實例對象的屬性,對原型無法造成影響。
若修改的屬性為繼承自原型的,屬性值類型為引用值,則對引用值的修改又分兩種情況
是直接對該修改的屬性賦值 => 此情況僅僅修改的是實例對象的該屬性,無法對原型造成影響
對該修改的屬性添加內容或去除內容,而不是對其重新賦值 => 此情況會對原型造成影響。如下例
Person.prototype = {
has : [1, 2, 3]
}
function Person () {
this.name = "shoujing";
}
var person1 = new Person();
var person2 = new Person();
person1.has.push(4);
//person1 和 person2都改變了,因為person1的修改影響到了原型,進而影響到了另一個實例對象
console.log(person1.has); //[1, 2, 3, 4]
console.log(person2.has); // [1, 2, 3, 4]
1
2
3
4
5
6
7
8
9
10
11
12
查
查詢過程如下,首先看構造函數中是否有要查詢的屬性,若有,則直接返回,若沒有,則看其原型有沒有要查詢的屬性,若沒有,則再看原型的原型上是否有要查詢的屬性,以此順序在原型鏈上查詢,若一直到原型鏈頂端後仍沒有要查詢的屬性,則返回undefined
絕大多數對象最終都會繼承自Object.prototype
null,和undefined是沒有原型的
對象繼承史
傳統模式
即正常的通過構造函數創建實例對象,來繼承原型–>原型鏈
缺點:繼承了過多不必要的屬性
借用構造函數
原理是通過call/apply改變構造函數內this對象的引用
不能繼承借用構造函數的原型
每次構造函數都要多走一個函數
舉例
function Person (name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student (name, age, sex, grade){
Person.call(this, name, age, sex)//call傳參
//Person.apply(this, [name, age, sex])//apply傳參使用數組
this.grade = grade;
}
var student = new Student("張三", 18, "male", 2107);
console.log(student);
1
2
3
4
5
6
7
8
9
10
11
12
13
共享原型
即將其他構造函數的原型直接賦值給本構造函數的原型
兩個原型會相互影響,更改其中一個原型,另一個對應的原型也會被更改。
舉例
Person.prototype = {
name : "shoujing",
age : 18,
sex : "male"
}
function Person () {
this.hooby = "sing";
}
Student.prototype = Person.prototype;
function Student() {
this.grade = 2017;
}
Son.prototype = Person.prototype;
function Son () {
this.grand = "luo";
}
var person = new Person();
var student = new Student();
var son = new Son();
console.log(person.age);//18
console.log(student.age);//18
console.log(son.age);//18
//原型繼承成功
Student.prototype.age = 20;//更改其中一個原型的age屬性
console.log(student.age);//20
console.log(son.age);//20
//繼承Person原型的Student和Son相互影響
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
聖杯模式
每次繼承的都是新創建的F構造函數實例,相互之間不會影響。其實此處針對F形成了閉包,Target引用了F,導致F不會銷毀。
正常函數形式:
function inherit (Target, Origin) {
// 借用F這個中間量來繼承,而不是直接共享原型
var F = function (){}
F.prototype = Origin.prototype;
Target.prototype = new F();
// 自定義構造函數原型時,同時要更正自定義原型的constructor,否則一般默認為Object(),次函數若不指定constructor,則constructor為Origin
Target.prototype.constructor = Target;
Target.prototype.uber = Origin; //記錄真正繼承的是誰
}
1
2
3
4
5
6
7
8
9
10
閉包形式(推薦,YUI)
var inherit = (function(){
var F = function (){};
return function (Target, Origin) {
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin;
}
})();
1
2
3
4
5
6
7
8
9
命名空間
我們可以利用對象(閉包)創建命名空間來管理變數,防止污染全局,適用於模塊開發,舉個例子:
var workSpace = {
person1: {
name: "one",
age: 18
},
person2: {
name: "two",
age: 20
}
}
// 這樣兩個人雖然有同名變數,但不會相互影響,因為位於不同命名空間
// 訪問第一個人的姓名
console.log(workSpace.person1.name); // one
console.log(workSpace.person2.name); //two
1
2
3
4
5
6
7
8
9
10
11
12
13
14
對象枚舉
prop in obj
var obj = {
name : "123",
age : 18,
sex : "male",
height : 180,
weight : 75,
}
for (var prop in obj) {
console.log(obj[prop]);
}
1
2
3
4
5
6
7
8
9
10
obj.hasOwnProperty(『prop』);
該方法的作用是來判斷對象obj的自身屬性中是否含有屬性prop,自身屬性是在構造函數中生成的或者實例對象後來自己添加的,而繼承屬性則是從原型上繼承的屬性,所以該方法就是判斷該屬性是從原型繼承來的還是自身的。返回結果為true,則為自身屬性。
var obj = {
name : "123",
age : 18,
sex : "male",
height : 180,
weight : 75,
__proto__ : {
lastName : "deng"
}
}
for (var prop in obj) {
if(obj.hasOwnProperty(prop)) {
console.log(obj[prop]);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object instanceof Object;
instanceof操作符用來判斷object實例對象是否為Object構造函數創建的,舉例
Person.prototype.age = 18;
function Person () {
this.name = "lyl";
}
var person = new Person();
console.log(person instanceof Person); // true
console.log(new Object() instanceof Person); //false
---------------------
作者:shoujing1001
原文:https://blog.csdn.net/shoujing1001/article/details/85487979
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
打開今日頭條,查看更多圖片※默認終端 + iTerm2 + agnoster theme +……打造macOS終端
※前端項目中常用es6知識總結——箭頭函數及this指向、尾調用優化
TAG:程序員小新人學習 |