JS高級
js面向對象編程
面向對象介紹
什麼是對象
- 是單個事物的抽象
- 對象是個容器, 封裝了屬性和方法
- 數據集或功能集
- ECMAScript-262把對象定義爲:無序屬性的集合, 其屬性可以包含基本值/對象或者函數
- 對象的屬性和方法, 叫做成員
- 對象的每個屬性或方法都有一個名字, 而每個名字都映射到一個值
什麼是面向對象
-
特點: 封裝/繼承/多態(抽象)
封裝性: 對象是將數據與功能組合到一起, 即封裝
繼承性: 自己沒有, 別人有, 拿過來爲自己所用, 併成爲自己的東西
多態性: -
有點: 靈活/可複用/高度模塊化
程序中面向對象的基本體現
創建對象
// 1. 字面量
var obj = {
name: 'zs',
age: 18
}
// 2. 使用object對象創建對象, Object對象是所有對象的祖宗, 函數也是對象
var obj = new Object();
// 3. 自定義構造函數
function Person(name, age) {
this.name = name,
this.age = age
}
var zs = new Person('zs', 18);
new 做的事情分爲四個步驟
1. new 在內存中開闢一片空間, 創建了一個新的對象
2. this 指向了這個新創建出來的對象
3. 讓構造函數執行
4. 返回新創建出來的那個對象
// 4. 工廠函數
function createPerson(name, age) {
return {
name:name,
age: age
}
}
簡單方式
簡單方式的改進: 工廠函數
更優雅的方式: 構造函數
構造函數的問題
原型
更好的解決方案: prototype
構造函數/實例/原型三者之間的關係
- 每一個構造函數, 在加載到內存的時候, 瀏覽器會幫助我們自動創建一個對象, 這個對象我們稱之爲構造函數的原型對象, 簡稱原型
- 我們通過構造函數的prototype屬性可以訪問到自己的原型對象
- 原型對象身上有一個constructor屬性, 可以訪問到自己對應的構造函數
- 構造函數的實例, 可以調用原型對象上的屬性, 如果構造函數裏也有這個屬性, 那麼會執行構造函數上的, 如果構造函數沒有這個屬性, 才執行原型對象上的
屬性成員的搜索原則: 原型鏈
實例對象讀寫原型對象成員
更簡單的原型語法
原生對象的原型
原型對象使用建議
函數進階
函數的定義方式
函數聲明
函數表達式
函數聲明與函數表達式的區別
函數的調用方式
函數內this指向的不同場景
函數也是對象
call/apply/bind
call
apply
bind
小結
函數的其他成員
函數的靜態成員和實例成員
高階函數
作爲參數
作爲返回值
函數閉包
作用域/作用域鏈/預解析
什麼是閉包
閉包的思考題
正則表達式
正則表達式簡介
什麼是正則表達式
正則表達式的作用
正則表達式的特點
正則表達式的測試
正則表達式的組成
元字符串
常用元字符串
限定符
其他
案例
js中使用正則表達式
創建正則對象
參數
正則匹配
正則提取
正則替換
案例:表單驗證
實現繼承的方式
var base = {
name:'',
age: '',
sayHello() {
console.log('hello');
}
}
// 1. 混入式繼承(用的極少)
var obj = {};
for(var k in base) {
obj[k] = base[k];
}
// 2. 原型繼承(用的比較多)
function Person() {
}
Person.prototype = base;
var p = new Person();
function Student() {
}
Student.prototype = Person.prototype;
// 3. 經典繼承 Object.create
// 下面的代碼的作用
// 1. 創建一個新對象obj1
// 2. 把obj1的原型(__proto__)設置爲base
// 傳進去的base就是我們要繼承的對象, obj1對象的原型上面, 就是base上面的成員
var obj1 = Object.create(base);
console.log(obj1);
// 4. 借用構造函數繼承(call apply)(繼承的是屬性, 無法繼承到原型上的方法)
function Person() {
this.name = "";
this.age = ""
}
function Student() {
Person.call(this);
}
var stu = new Student();
console.log(stu);
// 5. 組合式繼承 = 原型繼承 + 借用構造函數繼承
function Person() {
this.name = '喵喵';
this.age = '';
this.play = [1,2,3];
}
Person.prototype.sayHello = function () {
console.log('我最棒, 真的');
}
function Student() {
this.color = 'yellow';
Person.call(this);
}
Student.prototype = new Person();
var p = new Person();
var s = new Student();
var h = new Student();
s.play.push(4); // 如果不加構造函數繼承的話, 這裏的改變, 也會改變其他實例上的這個數組, 這就改的太喪心病狂了. 加了以後, 就可以隨便改了, 怎麼改都是改自己實例, 而不會連其他實例的都一起改變
s.color = 'ss';
console.log(p);
console.log(s);
console.log(h);
h.sayHello();
// 6. es6實現繼承的方法
class Person{
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log('hello');
}
static sayHi() {
console.log('hi');
}
}
// extends 關鍵字使用的其實就是 組合式繼承
class Student extends Person{
constructor() {
// 在子類構造函數中, 第一步事情, 調用父類的構造函數
super(); // 相當於es5的Person.call(this); 不寫會報錯
this.stuNo = 10000;
}
}
let stu = new Student('喵喵', 0.67);
console.log(stu.stuNo);
stu.sayHello();
Student.sayHi(); // Person的靜態成員也被繼承了