一、對象 1.字面量創建對象 2.數據描述和存取描述設置 3.get和set 4.數據描述和存取描述檢查 二、prototype 1.prototype釋義 2.prototype與__proto__ 3.prototype之共用性 4.prototype之繼承性 三、類 1.類的封裝 1.混合的構 ...
一、對象
1.字面量創建對象
var person = {
name: "sun",
age: 18,
work: function () {
console.log(this.name + "is working...");
},
address: {
home: "大屯裡xxx路xxx小區xx單元xxx室",
phone: "123456789",
}
};
person.work();
console.log(person.address.home);
2.數據描述和存取描述設置
var person = {
age: 18,
address: {
home: "大屯裡xxx路xxx小區xx單元xxx室",
phone: "123456789",
}
};
Object.defineProperties(person, {
name: {
value: "sun", // 該屬性的值,可被讀取
writable: true, // 表示能否修改屬性的值,預設值為true
configurable: true, // 表示能否delete該屬性並重新定義,直接在對象上定義的屬性預設值為true
enumerable: true // 表示能否通過for-in枚舉,直接在對象上定義的屬性預設值為true
},
work: {
value: function(){
console.log(this.name + "is working...");
},
// 通過Object.defineProperty和Object.defineProperties定義屬性,
// 如果沒有聲明writable、configurable、enumerable,它們的預設值都是false
}
});
person.work();
console.log(person.address.home);
3.get和set
var circle = {
value: 10,
get girth(){
return 2 * 3.14 * this.R
},
get area(){
return 3.4 * this.R * this.R
},
};
Object.defineProperty(circle, "R", {
get : function () {
return this.value;
},
set : function (val) {
console.log("半徑被修改了!");
this.value = val;
}
});
circle.R = 100;
console.log("girth: " + circle.girth + "area: " + circle.area);
4.數據描述和存取描述檢查
var circle = {
R: 10,
// __proto__: null,
get area(){
return 3.4 * this.R * this.R
},
};
Object.defineProperty(circle, "site", {
value: [0, 2.2, 4.1],
// enumerable: true, // 是否可配置(讀取),不設置為true時,Object.keys(circle))和Object.values(circle))將獲取不到該鍵值對
});
console.log("R" in circle); // 檢查屬性
console.log(circle.hasOwnProperty("R")); // 檢查自有的屬性
console.log(circle.propertyIsEnumerable("R")); // 檢查屬性是否是可枚舉的
// Object對象的方法
console.log(Object.keys(circle));
console.log(Object.values(circle));
console.log(Object.getOwnPropertyNames(circle)); // 檢查對象自身所有屬性
console.log(Object.getOwnPropertyDescriptor(circle, "R")); // 得到circle對象關於R屬性的描述
二、prototype
1.prototype釋義
- 每一次創建函數,解析器都會向函數中添加一個屬性:prototype
- 如果函數作為普通函數調用prototype,沒有任何作用
- 當該函數以構造函數的形式調用時,它會有一個隱含的屬性__proto__指向其原型對象
- 每個實例有各自的__proto__指向原型對象的prototype, 也就是原型對象中的屬性和方法被調用函數"共用"
- 當類的原型對象prototype指向的記憶體地址發生改變時,已創建實例的__proto__ !== prototype,也就是不會被覆蓋。而新創建的實例仍然是__proto__ === prototyp
function Person(name, age) {
this.name = name;
this.age = age;
}
// Person.prototype.gender = "male";
// Person.prototype.sayHello = function () {
// return this.name + ", " + this.age + "years old."
// };
Person.prototype = {
gender: "male",
sayHello: function () {
return this.name + ", " + this.age + "years old."
}
};
var p1 = new Person("孫悟空", 2000);
p1.sayHello();
console.log(Person.prototype);
console.log(Person.prototype.constructor === Person);
2.prototype與__proto__
function Person() {}
var obj1 = { gender: "male"}; // 創建兩個記憶體地址
var obj2 = { age: 200 };
Person.prototype = obj1;
var p1 = new Person();
console.log(p1.__proto__ === Person.prototype);
console.log(p1.__proto__.gender);
console.log(Person.prototype);
Person.prototype = obj2;
var p2 = new Person();
console.log(p2.__proto__.age);
console.log(Person.prototype);
console.log(p1.__proto__.age); // undefined
console.log(p2.__proto__.gender); // undefined
console.log(p1.__proto__ === Person.prototype); // false,表示當prototype指向的記憶體地址改變時,已經創建的實例對象的__proto__仍指向原來的記憶體地址
console.log(p2.__proto__ === Person.prototype);
function Person() {}
Person.prototype = {name: "xxx", age: 100,};
var p1 = new Person();
console.log(p1.__proto__.name);
Person.prototype = { price: 998,};
var p2 = new Person();
console.log(p2.__proto__.price);
console.log(p1.__proto__.price); // undefined
console.log(p2.__proto__.name); // undefiend
console.log(p1.__proto__ === Person.prototype); // false, 原型對象的記憶體地址引用已發生改變
console.log(p1.__proto__.age); // __proto__指向的記憶體地址被保留
console.log(p2.__proto__ === Person.prototype); // true
function Person() {}
Person.prototype = { price: 60 };
var p1 = new Person();
Person.prototype = { price: 998};
var p2 = new Person();
console.log(p1.__proto__ === Person.prototype); // 依然是false
console.log(p2.__proto__ === Person.prototype); // true
3.prototype之共用性
// prototype非常類似python中的靜態屬性和靜態方法。每個實例都可以訪問同一塊記憶體空間。
function Person() {}
Person.prototype = {price: 60};
var p1 = new Person();
var p2 = new Person();
console.log(p1.__proto__.price);
console.log(p2.__proto__.price);
console.log(Person.prototype.price);
4.prototype之繼承性
// 當訪問實例對象的一個屬性或方法時,它會先在對象自身中查找,如果有則直接使用;如果沒有則在原型對象中繼續查找,如果有則直接使用
function Person() {}
Person.prototype = {price: 60};
var p1 = new Person();
var p2 = new Person();
console.log(p1.price);
console.log(p2.price);
console.log(Person.prototype.price);
三、類
1.類的封裝
// 字面量方法(工廠方法) -- 直接在var obj = {}內部寫代碼,缺點是只實例化一次
// 構造函數方法 -- 只用構造函數聲明this,缺點是可擴展性差,數據重覆
// 原型方法 -- 只用prototype聲明共有的屬性和方法,缺點是實例的數據相同,不滿足多態
1.混合的構造函數/原型方法
// 最廣泛的使用方法
function Person(name, age) {
this.name = name;
this.age = age;
}
// prototype寫在外面是為了保證其動態增加公共屬性和方法
Person.prototype.sayHello = function () {
console.log(this.name + ", " + this.age + " years old."); // 把共有的屬性和方法封裝到prototype中
};
var p = new Person("孫悟空", 2000);
p.sayHello();
// 我把它寫給Person的屬性,讓父類也能夠訪問
function Person(name, age) {
Person.group = Person.prototype.group = "西天取經組";
Person.toString = Person.prototype.toString = function (){
console.log("Person: " + Person.group)
};
this.name = name;
this.age = age;
this.sayHello = function () {
console.log(this.name + ", " + this.age + "years old.")
};
}
var person = new Person("孫悟空", 2000);
console.log(person.constructor); // 檢查構造器函數
console.log(person instanceof Person); // 檢查是否為其原型類
person.sayHello();
Person.toString();
2.動態原型方法
// 也是常用的方法
function Person(name, age) {
this.name = name;
this.age = age;
if (typeof Person._initialized === "undefined"){
Person.prototype.sayHello = function () {
console.log(this.name + ", " + this.age + " years old.");
};
Person._initialized = true;
}
}
var p = new Person("孫悟空", 2000);
p.sayHello();
3.混合工廠方法
// 混合工廠方法 -- 存在與工廠方法類似的問題,不建議使用
function Person(name, age) {
var obj = {};
obj.name = name;
obj.age = age;
obj.sayHello = function () {
console.log(this.name + ", " + this.age + " years old.");
};
return obj
}
var p = new Person("孫悟空", 2000);
p.sayHello();
4.再探討類結構
function Person(name, age) {
// 靜態屬性
Person.group = "西天取經四人組,暗合金木水火土";
// 靜態方法
Person.introduce = function () {
console.log("貧僧自東土大唐而來")
};
// 實例屬性
this.name = name;
this.age = age;
// 實例方法,應該寫在prototype中
this.say = function () {
console.log("hello, i'm " + this.name);
};
Person.prototype.introduce = Person.introduce; // 此時Person類和其實例都可以使用introduce方法
// 父類使用實例方法
Person.example = Person.prototype.example = function (self) {
self = self || this;
console.log(self.name + " " + self.age);
}
}
// 在python中,實例可以訪問父類的屬性和方法,父類也可以使用實例方法
// 在java和js中,實例不能調用父類的靜態屬性和靜態方法,父類不能使用實例方法
// 如果想讓實例和父類共用一個屬性或者方法,就只能放到方法區並創建引用
var sun = new Person("孫悟空", 2000);
Person.introduce(); // 父類調用靜態方法
sun.say();
sun.introduce(); // 實例調用靜態方法
Person.example(sun); // 父類調用實例方法
sun.example(); // 子類調用實例方法
// 可見,prototype是父類和實例的溝通橋梁
2.自定義類
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function () {
console.log(this.name + ", " + this.age + "years old.")
};
}
function New(