12.JavaScript的面向?qū)ο?-創(chuàng)建對象方案

創(chuàng)建對象的方案

1.字面量方式

//使用字面量的方式創(chuàng)建三個對象
var p1 = {
  name: "小名",
  age: 30,
  sex: "male",
  running: function () {
    console.log(this.name + "is running!");
  },
};

var p2 = {
  name: "小紅",
  age: 25,
  sex: "female",
  running: function () {
    console.log(this.name + "is running!");
  },
};

var p3 = {
  name: "小麗",
  age: 30,
  sex: "female",
  running: function () {
    console.log(this.name + "is running!");
  },
};
console.log(p1)
console.log(p2)
console.log(p3)

image.png
缺點:
  • 1.做了很多重復工作
  • 2.這樣創(chuàng)建的對象沒有標出所屬的類別
  • 3.每創(chuàng)建一個對象,都需要在堆內(nèi)存中開辟一塊新內(nèi)存,存儲創(chuàng)建的running函數(shù)對象,如果創(chuàng)建100個對象,就創(chuàng)建了100running函數(shù),就需要100個這樣的堆內(nèi)存來存儲100個這樣的running函數(shù)對象

2.工廠模式

//使用工廠模式創(chuàng)建對象
function createPerson(name, age, sex) {
  var p = {};

  p.name = name;
  p.age = age;
  p.sex = sex;
  p.running = function () {
    console.log(this.name + "is running!");
  };

  return p;
}

var p1 = createPerson("小名", 30, "male");
var p2 = createPerson("小紅", 25, "female");
var p3 = createPerson("小麗", 30, "female");
console.log(p1);
console.log(p2);
console.log(p3);
image.png
缺點:
  • 這樣創(chuàng)建的對象沒有標出所屬的類別
  • 每創(chuàng)建一個對象,都需要在堆內(nèi)存中開辟一塊新內(nèi)存,存儲創(chuàng)建的running函數(shù)對象,如果創(chuàng)建100個對象,就創(chuàng)建了100running函數(shù),就需要100個這樣的堆內(nèi)存來存儲100個這樣的running函數(shù)對象

3.構造函數(shù)

3.1.什么是構造函數(shù)

當一個函數(shù)被new操作符調(diào)用的時候,這個函數(shù)就是構造函數(shù)

new操作符調(diào)用的作用
  • 1.在內(nèi)存中創(chuàng)建一個新的對象(空對象)
  • 2.這個對象內(nèi)部的[[prototype]]屬性會被賦值為該構造函數(shù)的prototype屬性的值
  • 3.構造函數(shù)內(nèi)部的this,會指向創(chuàng)建出來的新對象
  • 4.執(zhí)行構造函數(shù)內(nèi)部的代碼(函數(shù)體代碼)
  • 5.如果構造函數(shù)沒有返回非空對象,則返回創(chuàng)建出來的這個新對象
function person() {}

const p = new person();
console.log(p);
image.png

可以看到:通過構造函數(shù)創(chuàng)建出來的對象,有標注出來這個對象所屬的類的類名person的
說明,構造函數(shù)的調(diào)用創(chuàng)建出了一個有標明所屬類的類名的對象;那我們使用構造函數(shù)去創(chuàng)建對象,不就解決了創(chuàng)建出的帶向沒有標注出所屬類的類名的問題?

補充:
function person() {}
//1.當構造函數(shù)創(chuàng)建對象的時候,不需要傳參,可以省略小括號
const p1 = new person;
console.log(p1)

//2.獲取構造函數(shù)創(chuàng)建出來的對象所屬類的類名
console.log(p1.__proto__.constructor.name;

//3.為了區(qū)分構造函數(shù)和普通函數(shù)的區(qū)別,一般情況下,構造函數(shù)的名字會大寫
function Fish() {}
const fish = new Fish();

image.png

3.2使用構造函數(shù)創(chuàng)建對象

function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
  this.running = function () {
    console.log(this.name + "is running!");
  };
}

var p1 = new Person("小名", 30, "male");
var p2 = new Person("小紅", 25, "female");
var p3 = new Person("小麗", 30, "female");
console.log(p1);
console.log(p2);
console.log(p3);

image.png
優(yōu)點:
  • 通過構造函數(shù)創(chuàng)建出的對象,有標注出所屬類的類名
缺點:
  • 每創(chuàng)建一個對象,都需要在堆內(nèi)存中開辟一塊新內(nèi)存,存儲創(chuàng)建的running函數(shù)對象
  • 如果創(chuàng)建100個對象,就創(chuàng)建了100running函數(shù),就需要100個這樣的堆內(nèi)存來存儲100個這樣的running函數(shù)對象,浪費內(nèi)存

4.構造函數(shù)和原型相結合

4.1對象的原型理解

每個對象都有一個特殊的內(nèi)置屬性[[prototype]],指向一個對象,這個對象叫做對象的原型
  • 為了和函數(shù)的原型做區(qū)分,我們一般稱之為隱式原型對象

早起的ECMA是沒有規(guī)范如何去查看內(nèi)置屬性[[prototype]]這個原型對象的

  • 1.瀏覽器給對象提供了一個屬性__proto__,可以通過此屬性去查看對象的原型對象
  • 2.ES5提供了一個方法Object.getPrototype(obj),去查看目標對象的原型對象
var obj = { name: "why" };
console.log(obj.__proto__); //[Object: null prototype] {}
console.log(Object.getPrototypeOf(obj)); //[Object: null prototype] {}
console.log(obj.__proto__ === Object.getPrototypeOf(obj)); //true
image.png

可以看到通過__proto__Object.getPrototype(obj)獲取到的同一個對象的原型對象,也是同一個對象

原型的用處

當我們從一個對象中獲取一個屬性的時候,它會觸發(fā)[[get]]操作:

  • 1.在當前對象obj中查找此屬性,如果找到,就返回對應的值
  • 2.如果沒有找到,會去此對象的原型對象obj.__proto__上查找此屬性,如果找到,就返回對應的值
  • 3.如果沒有找到,繼續(xù)去原型對象的原型對象obj.__proto__.__proto__上查找此屬性,如果找到,就返回對應的值
  • ....
  • 4.直到找到Object.prototype為止,,此時Object.prototype.__proto__為null,則停止查找,返回undefined
var obj = { name: "why" };
obj.__proto__.age = 18;
console.log(obj.age); //18 雖然obj對象上沒有age屬性,但是在obj.__proto__對象上找到了age屬性,就返回其值18

4.2函數(shù)的原型理解

  • 函數(shù)作為一個對象而言,它也是有內(nèi)置屬性[[prototype]]這個隱式原型對象的

  • 作為函數(shù),函數(shù)還有一個屬性prototype,指向一個對象,我們一般稱之為顯式原型對象

  • 函數(shù)作為構造函數(shù),被new調(diào)用的時候,會把內(nèi)部創(chuàng)建的新的對象的[[prototype]]隱式原型對象指向構造函數(shù)的prototype顯示原型對象

function foo() {}

console.log(foo.__proto__); //{}
console.log(foo.prototype); //{}

//函數(shù)作為構造函數(shù),被new調(diào)用的時候,
//會把內(nèi)部創(chuàng)建的新的對象的[[prototype]]隱式原型對象指向構造函數(shù)的prototype顯示原型對象
const f = new foo();
console.log(f.__proto__); //{}
console.log(foo.prototype); //{}
console.log(f.__proto__ === foo.prototype); //true
image.png
image.png

4.3函數(shù)的原型對象prototype上的constructor

4.3.1.查看函數(shù)原型對象上有哪些屬性
function foo() {}

console.log(foo.prototype); //{}
console.log(Object.getOwnPropertyDescriptors(foo.prototype));
image.png

說明,函數(shù)原型對象上有一個屬性constructor,并且constructor屬性是不可枚舉的

function foo() {}
console.log(foo.prototype.constructor === foo); //true 函數(shù)原型對象上的constructor屬性指向函數(shù)本身
//獲取函數(shù)原型對象上的constructor函數(shù)的名字
console.log(foo.prototype.constructor.name); //foo  函數(shù)的名字可以通過訪問函數(shù)的name屬性獲取

說明:函數(shù)原型對象上的constructor屬性指向函數(shù)本身

4.4改變函數(shù)的prototype的指向

獲取一個對象的屬性,其實實在進行[[get]]操作:
先在當前對象上尋找屬性,找到就返回對應的值;找不到就去對象的隱式原型對象[[prototype]]對象上找
對象的隱式原型對象[[prototype]]指向其構造函數(shù)的prototype對象

foo.prototype.name = "why";
foo.prototype.age = 18;
var f = new foo();
console.log(f.name, f.age); //"why" 18

如果需要在函數(shù)的原型對象上添加很多屬性,一個一個添加太麻煩,可以通過改變函數(shù)prototype指向到一個新對象的方式添加

//可以直接改變函數(shù)的prototype的指向,指向一個新對象
foo.prototype = {
  name: 'why',
  age: 18
}
Object.defineProperty(foo.prototype, "constructor", {
  configurable: true,
  enumerable: false,
  writable: true,
  value: foo
})

console.log(foo.prototype)
console.log(Object.getOwnPropertyDescriptors(foo.prototype));
image.png

4.5使用構造函數(shù)和原型相結合創(chuàng)建對象

function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
}
Person.prototype.running = function () {
  console.log(this.name + "is running!");
};

var p1 = new Person("小名", 30, "male");
var p2 = new Person("小紅", 25, "female");
var p3 = new Person("小麗", 30, "female");
console.log(p1);
console.log(p2);
console.log(p3);

//當前對象上沒有running方法,會去對象的[[prototype]]隱式原型對象上去找,
//而對象的隱式原型對象正是創(chuàng)建對象的構造函數(shù)的prototype顯示原型對象
p1.running()
p2.running()
p3.running()
image.png
優(yōu)點:
  • 通過構造函數(shù)創(chuàng)建出的對象,有標注出所屬類的類名
  • 如果創(chuàng)建100個對象,只會創(chuàng)建一個running函數(shù),避免了內(nèi)存浪費

非常感謝王紅元老師的深入JavaScript高級語法讓我學習到很多 JavaScript 的知識

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容