單例模式
- 把描述同一個事物特徵的信息進行分類歸組,放到同一個命名空間下(減少全局變量的污染)
var name = "邢";
var age = 21;
var sex = "男";
var name = "劉";
var age = 21;
var sex = "男";
//
var xing = {
name: "邢",
age: 21,
sex: "男"
};
var liu = {
name: "劉",
age: 21,
sex: "男"
};
//描述同一個對象的所有屬性放在對象中,避免了全局變量的干擾,"單例模式"
var timer = 1;
var xing = {
bar: function () {
},
fn: function () {
}
};
// var timer = 1;
var liu = {
bar: function () {
}
};
-----------------------------------------------------
var name = 'erYa';
var age = 18;
var name = 'jinYu';
var age = 22;
let person1 = {
name: 'erYa',
age:18
};
let person2 = {
name: 'jinYu',
age: 22
}
高級單例模式
//高級單例模式
//jquery的源碼的外層是採用了高級單例模式
var utils = (function () {
var timer = 1;
var num = 100;
return {
bar: function () {
},
fn: function () {
}
}
})()
//與單例模式相比,想使用同一變量,可以避免污染全局
--------------------------------------------------
let person1 = (function(){
let fn = function(){};
let name= 'erYa';
return {
name:name,
age:18,
fn:fn
}
})()
let person2 = (function(){
let fn = function(){};
let name= 'jinYu';
let age = 18;
return {
name:name,
fn:fn
}
})()
let person3 = function(){
let name= 'jinYu';
let age = 26
return {
name:name,
fn:fn
}
}
console.log(person3) // 這是一個函數,不是person3的個人信息
工廠模式
- 如果用單例模式去寫很多個person就會變得很麻煩,就有了工廠模式 特點:批量生產
- 把實現相同功能的代碼封裝到函數裏,以後想運行這個方法,就直接執行這個函數就好了
- 高內聚:提高代碼的複用率
- 低耦合:減少頁面的重複代碼
//工廠模式: 函數的封裝
function createStudent(name, age) {
var obj = {}
obj.name = name;
obj.age = age;
return obj;
};
var xing = createStudent('邢', 21);
var liu = createStudent('劉', 21);
對象前言
javaScript
面向對象: 基於對象
對象: 萬物皆對象
類: 指具有相同特徵的一類事物
實例:具體的一個事物:
面向對象
- 把抽象的對象按照特點進行分類(大類/小類),把類的公共特徵進行提取和封裝,放到對應的類別中
- 類就是對對象的一種細分,和公共部分的抽取
- 在類中具體派生出來的具體事物就是類的實例,而且實例擁有自己私有的特徵,還擁有所屬類上的特徵
- 我們研究面向對象,其實就是研究對象、類、實例之間的關係和各自的知識點
構造函數模式(構造自定義類)
構造函數解決了實例的私有屬性
- var f=new Fn();
- new 操作符——把 new 放在函數的前面,函數執行
- 構造函數就是讓我們創建自定義類
- new 函數執行 叫做構造函數運行模式,此時的Fn就是Fn類(構造函數),函數執行之後的返回結果就是一個對象,叫做實例對象(f就是Fn的實例)
- 類就是函數數據類型的
- 實例是對象數據類型
- 構造函數中的this指向當前實例
- 如果這個函數需要參數,那麼這個需要有小括號,如果不需要參數,那麼小括號可以省略
- new後面的函數就是構造函數,也叫類;那麼通過new函數得到的返回值就叫實例;實例是構造函數new出來的;
> 鉤子函數:在初始化實例時會默認調用原型上的一些方法,那麼這些方法就是鉤子函數
運行原理
- 1、在代碼執行之前,函數中會首先默認創建一個空對象 {}
- 2、讓當前函數裏的this指向這個對象
- 3、代碼執行
- 4、默認return 這個對象
let f1 = new Fn(‘erYa’, 18);
//原型三句話
//f1就是Fn的實例
//fn就是函數,也是自定義(Fn)類
//f1就是實例化的對象//let f3 = new Fn;
如果構造函數不傳實參,可以省略執行小括號
new 函數執行 叫做構造函數運行模式,此時的Fn就是Fn類(構造函數),函數執行之後的返回結果就是一個對象,叫做實例對象(f就是Fn的實例)
function fn() {
// var obj = {};
// this = obj;
this.name = 100;
// return this;
};
var f = new fn;
fn();
console.log(f);
/*
剛纔那些都是js的內置類,但是我們也可以自己去自定義一些類
*/
// 構造函數(構造自定義類)
function Fn(name, age) {
/*
形成私有作用域
形參賦值
變量提升
1、默認生成一個空對象 {}
2、讓函數裏的this指向這個對象
3、代碼執行
4、默認return 這個對象
*/
this.name = name; // 給this增加鍵值對
this.age = age // 給this增加鍵值對
this.say = function(){}
return {}
}
// new: 他是js裏的關鍵字
Fn() // 普通函數運行
let f = new Fn() // {}
let f1 = new Fn('erYa', 18);
//原型三句話
//f1就是Fn的實例
//fn就是函數,也是自定義(Fn)類
//f1就是實例化的對象
let f2 = new Fn('jinYu', 18);
let f3 = new Fn;//如果構造函數不傳實參,可以省略小括號
let f4 = Fn;
console.log(f)
console.log(f1)
console.log(f2)
console.log(f1.age === f2.age)
console.log(f1.say === f2.say)
console.log(f3)
console.log(f4)
// new 函數執行 叫做構造函數運行模式,此時的Fn就是Fn類(構造函數),函數執行之後的返回結果就是一個對象,叫做實例對象(f就是Fn的實例)
構造函數和普通函數的不同
JS爲了區分構造函數和普通函數,一般將構造函數首字母大寫;
- 運行上的不同
- 普通函數–>形成私有作用域–>形參賦值–>變量提升–>代碼執行–>作用域是否銷燬
- 構造函數–>形成私有作用域–>形參賦值–>變量提升–>默認生成一個對象–>把this指向這對象–>代碼執行–>默認把這個對象return出去–>作用域是否銷燬
- 執行上的不同
- 構造函數如果不傳實參,可以不加小括號
- 構造函數如果手動return一個基本數據值,不能改變人家的返回值,但是手動return引用數據類型,可以改變構造函數的返回值,此時return的東西已經不是當前類的實例了【所以不要輕易修改構造函數的返回值】
function Fn(n) {
let m = 10;
this.total = m + n;
this.say = function () {
console.log(this.total)
}
}
let f1 = new Fn(10);
let f2 = new Fn(20);
console.log(f1.n)//undefined
console.log(f2.m)//undefined
console.log(f1.total)//20
f2.say()//30
console.log(f1 === f2)//false
創建實例的方式
- 字面量創建實例的方式
let num = 1;
let str = 'w';
- 構造函數創建實例的方式
// 實例創建
var num = new Number(1);
console.log(typeof num);// "object"
console.log(num instanceof Number);//true
console.log(num);//Number {1}
console.log(num+1);//2
console.log(num == 1 )//比較true,絕對比較就是false
// 如果是數字並且只有一個參數,代表數組的length;如果大於等於兩個,那麼是數組的每一項;
var arr = new Array(100,200);
console.log(arr);//[100, 200]
console.log(typeof Array);// "function"
var arr = new Array("a")
console.log(arr);//["a"]
------------------------------------------------------------------------
let ss = new Number(1)
console.log(ss)
console.log((1).toFixed(2)) // '1.00' 把數字轉換爲字符串,保留指定位小數
console.log(ss.toFixed(2)) // '1.00'
let w = new String(3) // 創建一個字符串的實例
console.log(w)
console.log(w.substr) // f//.substr字符串裏的方法
console.log(w instanceof String) // true
檢測當前實例是否屬於某個類
- 使用instanceof是檢測當前實例是否屬於某個類
- 實例 instanceof 類,如果實例是屬於這個類,那就返回true,反之就是false
- 通過字面量方式創建的基本數據類型值不是一個標準的實例,不能使用instanceof 進行檢測;引用數據類型創建的就是一個標準的實例,可以使用instanceof來進行檢測;
侷限性
- instanceof不能檢測基本數據類型,只能檢測引用數據類型的值
// 字面量方式創建變量
// var num = 1;// num 是Number的一個實例嗎?是
// console.log(num instanceof Number);// false
// var obj = {};
// console.log(obj instanceof Object);// true
// var ary = [];
// console.log(ary instanceof Array);// true
--------------------------------------------
function Fn(name, age){
this.name = name;
this.age = age;
}
let f1 = new Fn;
console.log(f1 instanceof Fn) // true
console.log(1 instanceof Fn) // false
給實例的類封裝公共方法需要注意的幾點
- 你自己封裝的方法不能與人家內置的方法同名
- 給你自己的方法加前綴
//去重
function myUnique(){
console.log(this)
let obj = {};
for (var i = 0; i < this.length; i++) {
if(obj[this[i]] !== undefined){
this[i] = this[this.length-1];
this.length--;
i--;
continue;
}
obj[this[i]] = this[i]
}
}
Array.prototype.myUnique = myUnique;
console.log(ary.myUnique().sort().reverse().slice(1,3))
// 內置類的鏈式擴展:如果你想用鏈式調用,前提是你的方法的返回值必須是當前類的實例
原型模式
- 原型模式: 構造函數解決了對象實例中私有屬性的問題,原有模式解決了對象實例中公有屬性的問題;如果將實例的私有屬性放到了公有屬性上,減少了堆內存的開闢;
原型解決了實例的公有屬性
- 每一個函數(普通函數,構造函數)都天生自帶一個prototype屬性,屬性值是一個對象,它裏面存儲的是實例的公有屬性(原型)
- 每一個原型都天生自帶一個constructor屬性,其屬性值指向當前原型所屬的類
- 每一個對象都天生自帶一個__proto__屬性,其屬性值指向當前實例所屬類的原型
原型鏈
- 在對象裏查找-一個屬性,先看自己私有的有沒有,如果自己沒有,就通過__ proto__ 屬性找 到當前所屬類的原型上,如果原型上有,就直接用,如果沒有,繼續通過原型的__ proto__ 繼續往所屬類的原型上找,直到找到Object類的原型上找,如果還沒有,就是undefined, 這種級- 一級一級向上查找就會形成原型鏈
function Fn(x, y) {
this.x = x;
this.y = y;
this.getX = function () {
console.log(this.x);
}
}
Fn.prototype.getX = function () {
console.log(this.x);
}
Fn.prototype.getY = function () {
console.log(this.y);
}
var f1 = new Fn(100, 200);
f1.__proto__.a = 300;
var f2 = new Fn(100, 200);
console.log(f1.getX == f2.getX);//false
console.log(f1.getY == f2.getY);//true
console.log(f1.x == f2.x);//true
console.log(Fn.prototype.getX === f1.getX);//false
console.log(Fn.prototype.getX === f1.__proto__.getX);//true
f1.getX()//100
Fn.prototype.getX();// undefined
f2.__proto__.getX();//undefined
Fn.prototype.getY();//undefined
console.log(f1.a);//300
console.log(f1.toString);//ƒ toString() { [native code] }
原型:
- window 是全局作用域中一個大的對象;window是Object的一個實例;
- Function是所有函數數據類型的基類(包括自己)
- Object是所有對象的基類
- 所有的函數都有prototype和__proto__屬性
- Object的原型的__proto__執行的是自己,js認爲自己指向自己沒有意義,就規定爲null
- 所有的函數數據類型(普通的函數、類【內置類,自定義類】)都是Function的一個實例,Function和Object都是Function的一個實例,那Object類的__proto__指向Function的原型
- 所有函數都是Function的實例, 那Function也是函數, 那他的__proto__指向的是自己的原型。它的原型是一個匿名函數,但是也是對象,正常使用即可
- 當函數作爲對象時,有length和name屬性
- length:代表形參的個數,name:代表函數的名字
- 當函數作爲對象時,有length和name屬性
- 如果一個對象你不知道誰構出來的, 那他的__proto__就指向Object內置類的原型( 所有類的原型都指向Object內置類的原型)
- 函數的三種角色: 普通函數 構造函數 普通對象
- 在正式場合函數是以函數的身份出場的( 這是人家的主角色)
- 類的特點: 多態、 繼承和封裝
- 給Fn的原型新增鍵值對;這就是原型擴展;
原型圖
- 原型鏈:單向不可逆
原型重定向
- 內置類的原型不能被重定向
- 可以覆蓋內置類原型上的方法
- 用新的空間地址覆蓋Fn原有的空間地址;會導致constructor的丟失;
- 重定向例子
function Fn() {
this.x = 100;
this.y = 100;
}
Fn.prototype.getX = function () {
console.log(this.x)
}
let f1 = new Fn;
Fn.prototype = {
getY: function () {
console.log(this.y)
}
};
let f2 = new Fn;
console.log(f1.getX)
console.log(f2.getX)
console.log(f1.constructor)
console.log(f2.constructor)
console.log(Fn.prototype)
阿里面試題(原型鏈、運算符優先級)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName(); //2
getName(); //5//4
Foo().getName(); //2//1
getName(); //4//1
new Foo.getName(); //2
new Foo().getName(); //1//3
new new Foo().getName(); //1//3
/*
1、成員訪問:尋找對象裏的屬性名所對應的屬性值就是成員訪問(19)
Fn.aa
2、new(帶參數列表):就是構造函數執行有括號(19)
3、new(無參數列表):就是構造函數執行沒有括號(18)
優先級一樣,從左到右運算
*/
</script>
</body>
</html>