1. 構(gòu)造函數(shù)和原型
1.1 對(duì)象的三種創(chuàng)建方式--復(fù)習(xí)
- 對(duì)象字面量的方式 :
let obj = {
name: '張三',
age: 23,
sayHi: function () {
console.log('hi!');
}
}
console.log(obj.name);
obj.sayHi();
- 使用 new 關(guān)鍵字的方式創(chuàng)建對(duì)象 :
let object = new Object();
object.name = '李四';
object.age = 24;
object.sayHello = function () {
console.log('say hello');
}
object.sayHello();
- 使用構(gòu)造函數(shù)創(chuàng)建對(duì)象:
function Star(name, age) {
this.name = name;
this.age = age;
this.print = function () {
console.log(name + age);
}
}
let star = new Star('王五', 35);
star.print();
1.2 靜態(tài)成員和實(shí)例成員
實(shí)例成員
- 實(shí)例成員就是構(gòu)造函數(shù)內(nèi)部通過
this添加的成員 如下列代碼中uname age sing就是實(shí)例成員,實(shí)例成員只能通過實(shí)例化的對(duì)象來訪問。
// 實(shí)例成員就是構(gòu)造函數(shù)內(nèi)部通過 this 添加的成員
function Star(name, age) {
this.name = name;
this.age = age;
this.sayHello = function () {
console.log('say hello');
};
}
// 實(shí)例成員只能通過實(shí)例化的對(duì)象來訪問
let star = new Star('張三', 23);
console.log(star.name);
console.log(star.age);
// 不可以通過構(gòu)造函數(shù)來訪問實(shí)例成員
console.log(Star.age); // undefined
// 靜態(tài)成員是在構(gòu)造函數(shù)本身上添加的成員,靜態(tài)成員只能通過構(gòu)造函數(shù)訪問不能通過實(shí)例對(duì)象訪問
Star.sex = '男';
console.log(star.sex); // undefined 實(shí)例對(duì)象調(diào)用靜態(tài)成員
console.log(Star.sex); // 男 構(gòu)造函數(shù)調(diào)用靜態(tài)成員
靜態(tài)成員
- 靜態(tài)成員 在構(gòu)造函數(shù)本身上添加的成員 如下列代碼中 sex 就是靜態(tài)成員,靜態(tài)成員只能通過構(gòu)造函數(shù)來訪問。
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function() {
console.log('我會(huì)唱歌');
}
}
Star.sex = '男';
var ldh = new Star('劉德華', 18);
console.log(Star.sex);//靜態(tài)成員只能通過構(gòu)造函數(shù)來訪問
1.3 構(gòu)造函數(shù)的問題
- 構(gòu)造函數(shù)方法很好用,但是存在浪費(fèi)內(nèi)存的問題。

1.4 構(gòu)造函數(shù)原型 prototype
構(gòu)造函數(shù)通過原型分配的函數(shù)是所有對(duì)象所共享的。
JavaScript規(guī)定,每一個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性,指向另一個(gè)對(duì)象。注意這個(gè)prototype就是一個(gè)對(duì)象,這個(gè)對(duì)象的所有屬性和方法,都會(huì)被構(gòu)造函數(shù)所擁有。我們可以把那些不變的方法,直接定義在
prototype對(duì)象上,這樣所有對(duì)象的實(shí)例就可以共享這些方法。
/**
* 構(gòu)造函數(shù)
* */
function Star(name, age) {
this.name = name;
this.age = age;
/**
* 還是會(huì)優(yōu)先調(diào)用它
*
*/
/*this.sing = function () {
console.log('我會(huì)唱歌嗎?');
};*/
}
console.dir(Star);
// 在構(gòu)造函數(shù)的原型對(duì)象里面 添加共享的方法
// 原型是一個(gè)對(duì)象是每個(gè)構(gòu)造函數(shù)中都會(huì)存在的一個(gè)對(duì)象
// 原型的作用是: 共享方法
// 一般情況下,我們的公共屬性定義在構(gòu)造函數(shù)中,但是公共的方法我們需要放到原型對(duì)象身上
Star.prototype.sing = function () {
console.log('我會(huì)唱歌');
};
let star = new Star('黎明', 23);
let s = new Star('張華自', 23);
console.log(star.sing() === s.sing()); // true
1.5 對(duì)象原型
對(duì)象實(shí)例都會(huì)有一個(gè)屬性
__proto__(對(duì)象原型)指向構(gòu)造函數(shù)的prototype原型對(duì)象,之所以我們對(duì)象可以使用構(gòu)造函數(shù)prototype原型對(duì)象的屬性和方法,就是因?yàn)閷?duì)象有__proto__原型的存在。
__proto__對(duì)象原型和原型對(duì)象prototype是等價(jià)的__proto__對(duì)象原型的意義就在于為對(duì)象的查找機(jī)制提供一個(gè)方向,或者說一條路線,但是它是一個(gè)非標(biāo)準(zhǔn)屬性,因此實(shí)際開發(fā)中,不可以使用這個(gè)屬性,它只是內(nèi)部指向原型對(duì)象prototype。

function Star(name, age) {
this.name = name;
this.age = age;
}
/**
* 在原型對(duì)象上添加一個(gè)共享方法
*/
Star.prototype.sing = function () {
console.log('我會(huì)唱歌');
}
let ldh = new Star('劉德華', 66);
// 對(duì)比原型對(duì)象和對(duì)象原型
console.log(ldh.__proto__ === Star.prototype); // true
1.6 constructor構(gòu)造函數(shù)
對(duì)象原型(
__proto__)和構(gòu)造函數(shù)(prototype)原型對(duì)象里面都有一個(gè)屬性constructor屬性 ,constructor我們稱為構(gòu)造函數(shù),因?yàn)樗富貥?gòu)造函數(shù)本身。
constructor主要用于記錄該對(duì)象引用于哪個(gè)構(gòu)造函數(shù),它可以讓原型對(duì)象重新指向原來的構(gòu)造函數(shù)。
一般情況下,對(duì)象的方法都在構(gòu)造函數(shù)的原型對(duì)象中設(shè)置。如果有多個(gè)對(duì)象的方法,我們可以給原型對(duì)象采取對(duì)象形式賦值,但是這樣就會(huì)覆蓋構(gòu)造函數(shù)原型對(duì)象原來的內(nèi)容,這樣修改后的原型對(duì)象 constructor 就不再指向當(dāng)前構(gòu)造函數(shù)了。此時(shí),我們可以在修改后的原型對(duì)象中,添加一個(gè) constructor 指向原來的構(gòu)造函數(shù)。
如果我們修改了原來的原型對(duì)象,給原型對(duì)象賦值的是一個(gè)對(duì)象,則必須手動(dòng)的利用constructor指回原來的構(gòu)造函數(shù)如:
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
// 很多情況下,我們需要手動(dòng)的利用constructor 這個(gè)屬性指回 原來的構(gòu)造函數(shù)
Star.prototype = {
// 如果我們修改了原來的原型對(duì)象,給原型對(duì)象賦值的是一個(gè)對(duì)象,則必須手動(dòng)的利用constructor指回原來的構(gòu)造函數(shù)
constructor: Star, // 手動(dòng)設(shè)置指回原來的構(gòu)造函數(shù)
sing: function() {
console.log('我會(huì)唱歌');
},
movie: function() {
console.log('我會(huì)演電影');
}
}
var zxy = new Star('張學(xué)友', 19);
console.log(zxy)
以上代碼運(yùn)行結(jié)果,設(shè)置constructor屬性如圖:

如果未設(shè)置constructor屬性,如圖:

1.7 原型鏈
- 每一個(gè)實(shí)例對(duì)象又有一個(gè)
proto屬性,指向的構(gòu)造函數(shù)的原型對(duì)象,構(gòu)造函數(shù)的原型對(duì)象也是一個(gè)對(duì)象,也有proto屬性,這樣一層一層往上找就形成了原型鏈。

function Star(name, age) {
this.name = name;
this.age = age;
}
console.log(Star.prototype); // 構(gòu)造函數(shù)中的原型對(duì)象 原型對(duì)象中有對(duì)象原型 __proto__ 指向 Object
/**
* Star.prototype.__proto__ 構(gòu)造函數(shù)中原型對(duì)象 的對(duì)象原型 指向 Object 的原型對(duì)象
* 我們Star原型對(duì)象里面
*/
console.log(Star.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__); // null Object的 原型對(duì)象的對(duì)象原型指向的是 null
console.dir(Object);
1.8 構(gòu)造函數(shù)實(shí)例和原型對(duì)象三角關(guān)系
1.構(gòu)造函數(shù)的prototype屬性指向了構(gòu)造函數(shù)原型對(duì)象;
2.實(shí)例對(duì)象是由構(gòu)造函數(shù)創(chuàng)建的,實(shí)例對(duì)象的__proto__屬性指向了構(gòu)造函數(shù)的原型對(duì)象;
3.構(gòu)造函數(shù)的原型對(duì)象的constructor屬性指向了構(gòu)造函數(shù),實(shí)例對(duì)象的原型的constructor屬性也指向了構(gòu)造函數(shù)。

function Star(name, age) {
this.name = name;
this.age = age;
}
let star = new Star('張三', 23);
// 1. 構(gòu)造函數(shù)中的 prototype 指向了原型對(duì)象
console.log(Star.prototype);
// 2. 原型對(duì)象中constructor指向了構(gòu)造函數(shù)
console.log(Star.prototype.constructor);
// 3. 構(gòu)造函數(shù)指向?qū)ο髮?shí)例 ,對(duì)象實(shí)例中的 __proto__(對(duì)象原型) 指向了原型對(duì)象
console.log(star.__proto__);
console.log(star.__proto__.constructor);
1.9 原型鏈和成員的查找機(jī)制
- 任何對(duì)象都有原型對(duì)象,也就是
prototype屬性,任何原型對(duì)象也是一個(gè)對(duì)象,該對(duì)象就有proto屬性,這樣一層一層往上找,就形成了一條鏈,我們稱此為原型鏈;
- 當(dāng)訪問一個(gè)對(duì)象的屬性(包括方法)時(shí),首先查找這個(gè)對(duì)象自身有沒有該屬性。
- 如果沒有就查找它的原型(也就是
__proto__指向的prototype原型對(duì)象)。- 如果還沒有就查找原型對(duì)象的原型(Object的原型對(duì)象)。
- 依此類推一直找到
Object為止(null)。__proto__對(duì)象原型的意義就在于為對(duì)象成員查找機(jī)制提供一個(gè)方向,或者說一條路線。
/**
* @param name
* @param age
* @constructor
*/
function Star(name, age) {
this.name = name;
this.age = age;
}
Star.prototype.sing = function () {
console.log('我會(huì)唱噶');
}
Star.height = 178;
console.log(Star.height);
let star = new Star('張三', 23);
// star.sex = '男'; // 1.先在實(shí)例對(duì)象上查找
// Star.prototype.sex = '男'; // 2. 再在原型對(duì)象上找
Object.prototype.sex = '女'; // 3. 最后在Object的原型對(duì)象上找
console.log(star.sex);
star.sing();
star.sex = '男';
console.log(star.sex); // 男
// 對(duì)象成員查找是根據(jù)原型鏈的機(jī)制進(jìn)行查找
console.log(Object.prototype); // 這里面有個(gè)toString方法
console.log(Star.prototype);
console.log(star.toString());
1.10 原型對(duì)象中this指向
- 構(gòu)造函數(shù)中的
this和原型對(duì)象的this,都指向我們new出來的實(shí)例對(duì)象:
let that;
/**
* 原型對(duì)象的 this 指向問題
* @param name
* @param age
* @constructor
*/
function Star(name, age) {
this.name = name;
this.age = age;
console.log("創(chuàng)建實(shí)例對(duì)象" + that === this);
}
Star.prototype.sing = function () {
// 這里的this指向的是誰? 指向的是函數(shù)的調(diào)用者 當(dāng)前的實(shí)例對(duì)象
console.log('我會(huì)唱歌');
console.log(this);
console.log("創(chuàng)建實(shí)例對(duì)象" + that === this);
that = this;
}
let star = new Star('張三', 23);
console.log(star);
star.sing();
// 1. 在構(gòu)造函數(shù)中,里面this指向的是對(duì)象實(shí)例
// 2. 原型對(duì)象函數(shù)里面的this指向的是函數(shù)的調(diào)用者,但
// 是不管是構(gòu)造函數(shù)中的this還是原型對(duì)象中的this都是指向的實(shí)例對(duì)象
1.11 通過原型為數(shù)組擴(kuò)展內(nèi)置方法
console.log(Array.prototype); // 打印Array構(gòu)造函數(shù)中的原型對(duì)象
/**
* 擴(kuò)展求和函數(shù)
*/
Array.prototype.sum = function () {
let sum = 0;
for (let i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
};
let arr = [100, 200, 300, 400, 500];
console.log(arr.sum());
/**
* 錯(cuò)誤的追加方式
*/
Array.prototype = function () {
let sum = 0;
for (let i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
};
// 注意:在數(shù)組和字符串內(nèi)置對(duì)象中不能給原型對(duì)象覆蓋操作 Array.prototype = {} 只能是采取 Array.prototype.函數(shù)或者屬性名 = function(){} 的方式
2. 繼承
2.1 call()
call()可以調(diào)用函數(shù);call()可以修改this的指向,使用call()的時(shí)候 參數(shù)一是修改后的this指向,參數(shù)2,參數(shù)3..使用逗號(hào)隔開連接。
// 1.call() 可以調(diào)用函數(shù)
function fn(x, y) {
console.log('call()可以調(diào)用函數(shù)和改變函數(shù)的this指向');
console.log(this);
console.log(x + y);
}
// fn.call();
let o = {
name: 'andy'
}
// 2.call() 可以改變函數(shù)的this的指向
fn.call(o, 1, 2);
2.2 子構(gòu)造函數(shù)繼承父構(gòu)造函數(shù)中的屬性
先定義一個(gè)父構(gòu)造函數(shù);
再定義一個(gè)子構(gòu)造函數(shù);
子構(gòu)造函數(shù)繼承父構(gòu)造函數(shù)的屬性(使用call方法)。
/**
* 1. 父構(gòu)造函數(shù)
* @param name
* @param age
* @constructor
*/
function Father(name, age) {
this.name = name;
this.age = age;
}
/**
* 2. 子構(gòu)造函數(shù)
* @param name
* @param age
* @constructor
*/
function Son(name, age) {
/**
* 這里的意思是將子構(gòu)造函數(shù)的this、指向了父構(gòu)造函數(shù)的this
*/
Father.call(this, name, age);
}
let son = new Son('李四', 24);
console.log(son);
2.3 借用原型對(duì)象繼承方法
先定義一個(gè)父構(gòu)造函數(shù);
再定義一個(gè)子構(gòu)造函數(shù);
子構(gòu)造函數(shù)繼承父構(gòu)造函數(shù)的方法(使用
call方法)。
/**
* 1. 父構(gòu)造函數(shù)
* @param name
* @param age
* @constructor
*/
function Father(name, age) {
this.name = name;
this.age = age;
}
/**
*
* 在父類的原型對(duì)象中添加一個(gè)共享方法
* */
Father.prototype.money = function () {
console.log('100000');
}
/**
* 2. 子構(gòu)造函數(shù)
* @param name
* @param age
* @constructor
*/
function Son(name, age) {
/**
* 這里的意思是將子構(gòu)造函數(shù)的this、指向了父構(gòu)造函數(shù)的this
*/
Father.call(this, name, age);
}
// 如果使用Son的原型對(duì)象 直接指向 Father的原型對(duì)象將會(huì)出現(xiàn)問題
// Son.prototype = Father.prototype;
// 讓Son的原型對(duì)象指向 Father 的實(shí)例對(duì)象
Son.prototype = new Father();
/**
* 此時(shí)再Son構(gòu)造函數(shù)中添加一個(gè)方法
* @type {Son}
*/
Son.prototype.exam = function () {
console.log('我要考試');
}
let son = new Son('李四', 24);
console.log(son);
let father = new Father();
console.log(father);
如上代碼結(jié)果如圖:

3. ES5新增方法
3.1 數(shù)組方法forEach遍歷數(shù)組
arr.forEach(function(value, index, array) {
//參數(shù)一是:數(shù)組元素
//參數(shù)二是:數(shù)組元素的索引
//參數(shù)三是:當(dāng)前的數(shù)組
})
//相當(dāng)于數(shù)組遍歷的 for循環(huán) 沒有返回值
演示案例 :
/**
* 數(shù)組方法 foreach,遍歷數(shù)組的值,并求所有值的和
*/
let arr = [1, 2, 3, 4, 5, 6, 6, 7, 8, 90];
let sum = 0;
arr.forEach((value, index, array) => {
console.log('每一個(gè)數(shù)組元素' + value);
console.log('每一個(gè)數(shù)組元素的索引' + index);
console.log('數(shù)組本身' + array);
sum += value;
});
console.log(sum);
3.2 數(shù)組方法filter過濾數(shù)組
var arr = [12, 66, 4, 88, 3, 7];
var newArr = arr.filter(function(value, index,array) {
//參數(shù)一是:數(shù)組元素
//參數(shù)二是:數(shù)組元素的索引
//參數(shù)三是:當(dāng)前的數(shù)組
return value >= 20;
});
console.log(newArr);//[66,88] //返回值是一個(gè)新數(shù)組
3.3 數(shù)組方法some
some 查找數(shù)組中是否有滿足條件的元素
var arr = [10, 30, 4];
var flag = arr.some(function(value,index,array) {
//參數(shù)一是:數(shù)組元素
//參數(shù)二是:數(shù)組元素的索引
//參數(shù)三是:當(dāng)前的數(shù)組
return value < 3;
});
console.log(flag);//false返回值是布爾值,只要查找到滿足條件的一個(gè)元素就立馬終止循環(huán)
3.4 案例 : 篩選商品案例

頁面結(jié)構(gòu)
<div class="search">
按照價(jià)格查詢: <input type="text" class="start"> - <input type="text" class="end">
<button class="search-price">搜索</button>
按照商品名稱查詢: <input type="text" class="product">
<button class="search-pro">查詢</button>
</div>
<table>
<thead>
<tr>
<th>id</th>
<th>產(chǎn)品名稱</th>
<th>價(jià)格</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
頁面樣式
table {
width: 400px;
border: 1px solid #000;
border-collapse: collapse;
margin: 0 auto;
}
td,
th {
border: 1px solid #000;
text-align: center;
}
input {
width: 50px;
}
.search {
width: 600px;
margin: 20px auto;
}
自定義數(shù)據(jù)
// 利用新增數(shù)組方法操作數(shù)據(jù)
let data = [{
id: 1,
pname: '小米',
price: 3999
}, {
id: 2,
pname: 'oppo',
price: 999
}, {
id: 3,
pname: '榮耀',
price: 1299
}, {
id: 4,
pname: '華為',
price: 1999
}, {
id: 5,
pname: '華為p40',
price: 6999
}, {
id: 6,
pname: '華為',
price: 1999
}];
操作邏輯
// 1. 獲取相應(yīng)的元素
let tbody = document.querySelector('tbody');
setData(data);
function setData(myData) {
// 先清空原來的數(shù)據(jù)
tbody.innerHTML = '';
// 2. 將數(shù)據(jù)渲染到頁面中
myData.forEach((value) => {
// 創(chuàng)建行元素
let tr = document.createElement('tr');
tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>';
tbody.appendChild(tr);
});
}
// 3. 根據(jù)價(jià)格查詢商品
// 搜索按鈕
let search_price = document.querySelector('.search-price');
// 開始輸入框
let start = document.querySelector('.start');
// 結(jié)束輸入框
let end = document.querySelector('.end');
search_price.addEventListener('click', () => {
let newData = data.filter((value) => {
return value.price >= start.value && value.price <= end.value;
});
setData(newData);
});
// 4. 根據(jù)商品名稱查找商品
// 如果查詢的是數(shù)組中唯一的元素,用some方法更加合適,因?yàn)樗业竭@個(gè)元素就不再進(jìn)行循環(huán)效率更高
let product = document.querySelector('.product');
let search_pro = document.querySelector('.search-pro');
search_pro.addEventListener('click', () => {
let arr = [];
data.some((value) => {
/**
* 找到之后就會(huì)終止循環(huán)
*/
if (value.pname === product.value) {
console.log(value);// 可以實(shí)現(xiàn)打印
arr.push(value);
return true; //
}
})
// 將拿到的數(shù)據(jù)渲染到頁面中
setData(arr);
});
3.5 some和forEach區(qū)別
如果查詢數(shù)組中唯一的元素, 用
some方法更合適,在some里面 遇到return true就是終止遍歷 迭代效率更高。在
forEach里面return不會(huì)終止迭代。
let arr = ['red', 'green', 'blue', 'pink'];
// 在forEach中return不會(huì)終止循環(huán)
/**
* value 滿足條件的值
*
* index 滿足條件的索引
*
* array 滿足條件的新數(shù)組
*/
arr.forEach((value, index, array) => {
if (value === 'green') {
console.log(value);
console.log('元素已經(jīng)找到');
return true;
}
console.log(666);
});
// some的效率會(huì)更高因?yàn)樵诘谝淮握业皆刂缶蜁?huì)終止循環(huán)
arr.some((value, index, array) => {
if (value === 'green') {
console.log(value);
console.log('元素已經(jīng)找到');
return true;
}
console.log(666);
});
3.6 trim方法去除字符串兩端的空格
-
trim()方法清除的是字符串兩端的空格,不清楚中間的空格。
<input type="text">
<button>提交</button>
<div></div>
/**
* trim 去除字符串兩端的空白字符
*/
let input = document.querySelector('input');
let btn = document.querySelector('button');
let div = document.querySelector('div');
btn.addEventListener('click', () => {
let strInput = input.value.trim();
// 首先判斷一下文本框的內(nèi)容是否為空
if (strInput === '') {
alert('輸入框不能為空!');
} else {
// 將文本框的內(nèi)容設(shè)置給 div
div.innerHTML = strInput;
console.log(strInput);
}
});
3.7 Object.keys() 獲取對(duì)象的屬性名
-
Object.keys(對(duì)象)獲取到當(dāng)前對(duì)象中的屬性名 ,返回值是一個(gè)數(shù)組:
var obj = {
id: 1,
pname: '小米',
price: 1999,
num: 2000
};
var result = Object.keys(obj)
console.log(result)//[id,pname,price,num]
3.8 Object.defineProperty
/**
*
* @type {{id: number, pname: string, price: number}}
*
* Object.defineProperty(obj , prop,descriptor)
*
* 1. obj 對(duì)象
*
* 2. prop 需要設(shè)置的屬性
*
* 3. 一個(gè)屬性配置對(duì)象
*
* 3.1 value: 設(shè)置屬性的值,默認(rèn)值是undefined
*
* 3.2 writable: 是否可以被重寫
*
* 3.3 enumerable:目標(biāo)屬性是否可以被枚舉。 true | false 默認(rèn)false
*
* 3.4 configurable:目標(biāo)屬性是否可以被刪除或是否可以在此修改 true | false 默認(rèn)是false
*/
let obj = {
id: 1,
pname: '小米',
price: 36669
};
Object.defineProperty(obj, 'num', {
value: 30000,
writable: false, // 是否可以被重寫
enumerable: false // 默認(rèn)值是false
});
Object.defineProperty(obj, 'pname', {
writable: false,// 是否可以被重寫
configurable: false // 是否可以被刪除 設(shè)置為false時(shí)則不再允許刪除這個(gè)屬性,不允許修改第三個(gè)參數(shù)中的特性
});
obj.pname = '華為';
console.log(obj);
console.log(Object.keys(obj));
delete obj.pname // 刪除 pname 屬性
console.log(obj);