javascript 筆記03(建立物件/原型模式/js 繼承/BOM)

weixin_33890499發表於2018-08-04

js 筆記3

接筆記2

13.建立物件

  1. 工廠模式
function createNewObject(name, age, job) {
  let o = new Object();
  o.name = name;
  o.age = age;
  o.job = job
  o.sayName = function() {
    console.log(name);
  }
  return o;
};

let me = createNewObject('Ethan', 24, 'java');

console.log(me.name);  // Ethan
  1. 建構函式模式
function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function () {
    console.log(this.name);
  }
};

let p = new Person('Ethan', 24, 'java')

console.log(p.name);  // Ethan

以這種方式呼叫建構函式實際上會經歷以下 4 個步驟:

  1. 建立一個新物件;
  2. 將建構函式的作用域賦給新物件(因此 this 就指向了這個新物件);
  3. 執行建構函式中的程式碼(為這個新物件新增屬性);
  4. 返回新物件。

通過建構函式模式建立出來的 p 物件有一個 constructor 屬性,該屬性指向 Person。並且通過建構函式可以將例項標識為一種特定的型別,這是工廠模式做不到的。

console.log(p.constructor == Person); // true
console.log(p instanceof Person);  //true

如果不使用 new 來宣告建構函式:this 就會指向 global 物件。

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function () {
    console.log(this.name);
  }
};

Person('zzz', 24, 'js');

global.sayName();

let o = new Object();

Person.call(o, 'yyy', 23, 'vue');

o.sayName();      // yyy
global.sayName(); // zzz

以上建構函式有個問題:使用建構函式的主要問題,就是每個方法都要在每個 例項上重新建立一遍 。

所以,做如下改進:

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = sayName;
};

function sayName() {
  console.log(this.name);
}

let p1 = new Person('Ethan', 24, 'java');

p1.sayName();  // Ethan

let p2 = new Person('aa', 22, 'bb');

console.log(p1.sayName == p2.sayName);  // true,而用之前的方式結果為 false

14.原型模式

是什麼:每個函式都有一個prototype(原型)屬性,該屬性可以看成一個指標,指向一個物件,這個物件的用途是包含了特定物件的所有例項共享的屬性和方法。可以把prototype理解為通過建構函式而建立的那個物件例項的原型物件。使用原型物件的好處就是可以讓所有物件例項共享它所包含的屬性和方法。所以,我們不必再建構函式中定義物件例項的資訊,而是將這些資訊直接新增到原型物件中。

因此,之前的建構函式可以寫成這樣:

function Person() {
};

Person.prototype.name = 'Ethan';
Person.prototype.age = 24;
Person.prototype.job = 'java';
Person.prototype.sayName = function () {
  console.log(this.name);
}

let p1 = new Person();

p1.sayName();  // Ethan

let p2 = new Person();

console.log(p1.sayName == p2.sayName); // true

理解:看圖吧,建構函式、建構函式的物件、prototypeconstructor[[Prototype]](_PROTO_)之間的愛恨情仇。

11131912-b511d8157066d984.jpg
image-20180804114016605

當我們呼叫一個物件的屬性時,都會進行一次搜尋,先搜尋該物件是否有相同名稱的屬性,再去搜尋該物件的原型是否有該屬性。所以如果我們給一個物件的原型已經有的屬性重新賦值,並不會影響物件的原型,只會應該該物件的屬性。可以通過hasOwnProperty()來判斷該屬性是例項屬性還是原型屬性。還有一個方法是in,比如"name" in person1,這個方法不管該屬性是在例項屬性中還是原型屬性中,都會返回true

除此之外,還有個方法返回物件所有的課列舉屬性。

function Person() {
};

Person.prototype.name = 'Ethan';
Person.prototype.age = 24;
Person.prototype.job = 'java';
Person.prototype.sayName = function () {
  console.log(this.name);
};

let keys = Object.keys(Person.prototype);
console.log(keys);  // [ 'name', 'age', 'job', 'sayName' ]

當然,上面的原型語法比較複雜,可以寫成下面這種相對簡單的:

function Person() {
};

Person.prototype = { 
  // constructor: Person, // 最後一段話也可以用這行代替
  name: 'Ethan',
  age: 24,
  job: 'java',
  sayName: function() {
    console.log(this.name);
  }
};

// 因為每建立一 個函式,就會同時建立它的 prototype 物件,這個物件也會自動獲得 constructor 屬性,所以加上這個
Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
});

原型模式的缺點:原型中所有屬性是被很多例項共享的,這種共享對於函式非常合適。 說簡單點就是修改一個物件的屬性會影響到另一個物件的該屬性。


15.組合使用建構函式模式和原型模式

建立自定義型別最常用的方法,建構函式模式用來定義例項屬性,原型模式用來定義方法和共享的屬性。這樣,每個例項都有自己的一份例項屬性的副本,但同時又共享著對方法的引用,節省了記憶體。還可以向建構函式傳遞引數。

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
}

Person.prototype = {
  constructor: Person,
  sayName: function () {
    console.log(this.name);
  }
}

let p1 = new Person('aa', 10, 'java');
let p2 = new Person('bb', 20, 'js');

p1.sayName(); // aa
p2.sayName(); // bb

p1.name = 'cc';

p1.sayName(); // cc
p2.sayName(); // bb

16.js 中的繼承

就說兩種吧:

  1. 組合繼承
function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
//繼承屬性 SuperType.call(this, name);
    this.age = age;
}
//繼承方法
SubType.prototype = new SuperType(); 
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){
    alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
   alert(instance1.colors);
instance1.sayName();
instance1.sayAge();
//"red,blue,green,black"
//"Nicholas";
//29
 var instance2 = new SubType("Greg", 27);
alert(instance2.colors);
instance2.sayName();
instance2.sayAge();
//"red,blue,green"
//"Greg";
//27
  1. 更完善的:寄生組合繼承
function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
 };
function SubType(name, age){
    SuperType.call(this, name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};

17.閉包

關於閉包,直接看這篇吧,我覺得說的挺易懂的。


18. BOM 中常用 api

window.innerHeigt

window.innerWidth

window.outerHeigt

window.outerWidth

window.location === document.location

location.assign

// 下面三行程式碼效果完全相同
window.location = "http://www.google.com";
location.href = "http://www.google.com";
location.assign = "http://www.google.com";

Navigator:獲取瀏覽器的資訊找這個。

screen:螢幕的資訊找這個。

history.go(-1)

history.go(1)

history.back()

history.forward()

相關文章