1.1 函數聲明
//ES5
function getSum(){}
function (){}//匿名函數
//ES6()=>{}
//如果{}內容只有一行{}和return關鍵字可省,
1.2 函數表達式(函數字面量)
//ES5
var sum=function(){}
//ES6
let sum=()=>{}//如果{}內容只有一行{}和return關鍵字可省,
1.3 構造函數
var sum=new GetSum(num1,num2)
1.4 三種方法的對比
1.函數聲明有預解析,而且函數聲明的優先級高于變量;
2.使用Function構造函數定義函數的方式是一個函數表達式,這種方式會導致解析兩次代碼,影響性能。第一次解析常規的JavaScript代碼,第二次解析傳入構造函數的字符串
2.ES5中函數的4種調用
在ES5中函數內容的this指向和調用方法有關
2.1 函數調用模式
包括函數名()和匿名函數調用,this指向window
function getSum() {
? ? console.log(this) //window
}
getSum()
(function() {
? ? ? console.log(this) //window
})()
? ? ? var getSum=function() {
? ? console.log(this) //window
}
getSum()
2.2 方法調用
對象.方法名(),this指向對象
var objList = {
? name: 'methods',?
? getSum: function() {?
? ? console.log(this) //objList對象
? }
}
objList.getSum()
2.3 構造器調用
new 構造函數名(),this指向構造函數
function Person() {
? console.log(this); //指向構造函數Person
}
var personOne = new Person();
2.4 間接調用
利用call和apply來實現,this就是call和apply對應的第一個參數,如果不傳值或者第一個值為null,undefined時this指向window
function foo() {
? console.log(this);
}
foo.apply('我是apply改變的this值');//我是apply改變的this值
foo.call('我是call改變的this值');//我是call改變的this值
3.ES6中函數的調用
箭頭函數不可以當作構造函數使用,也就是不能用new命令實例化一個對象,否則會拋出一個錯誤。
箭頭函數的this是和定義時有關和調用無關。
調用就是函數調用模式。
(() => {
? console.log(this)//window
})()
?
let arrowFun = () => {
? console.log(this)//window}
arrowFun()
?
let arrowObj = {
? arrFun: function() {
? (() => {?
? ? console.log(this)//arrowObj
? })()
? }
}
arrowObj.arrFun();
4.call,apply和bind
1.IE5之前不支持call和apply,bind是ES5出來的;
2.call和apply可以調用函數,改變this,實現繼承和借用別的對象的方法;
4.1 call和apply定義
調用方法,用一個對象替換掉另一個對象(this)
對象.call(新this對象,實參1,實參2,實參3.....)
對象.apply(新this對象,[實參1,實參2,實參3.....])
4.2 call和apply用法
1.間接調用函數,改變作用域的this值
2.劫持其他對象的方法
var foo = {
? name:"張三",?
? logName:function(){
? ? console.log(this.name);
? }
}
var bar={
? name:"李四"
};
foo.logName.call(bar);//李四
實質是call改變了foo的this指向為bar,并調用該函數
3.兩個函數實現繼承
function Animal(name){?
? this.name = name;?
? this.showName = function(){?
? ? console.log(this.name);
? }?
}?
function Cat(name){?
? Animal.call(this, name);?
}? ?
var cat = new Cat("Black Cat");?
cat.showName(); //Black Cat
4.為類數組(arguments和nodeList)添加數組方法push,pop
(function(){
? Array.prototype.push.call(arguments,'王五');
? console.log(arguments);//['張三','李四','王五']
})('張三','李四')
5.合并數組
let arr1=[1,2,3];
let arr2=[4,5,6];
Array.prototype.push.apply(arr1,arr2); //將arr2合并到了arr1中
6.求數組最大值
Math.max.apply(null,arr)
7.判斷字符類型
Object.prototype.toString.call({})
4.3 bind
bind是function的一個函數擴展方法,bind以后代碼重新綁定了func內部的this指向,不會調用方法,不兼容IE8。
var name = '李四'
var foo = {
? name: "張三",? ?
? logName: function(age) {?
? console.log(this.name, age);
? }
}
var fooNew = foo.logName;
var fooNewBind = foo.logName.bind(foo);
fooNew(10)//李四,10
fooNewBind(11)//張三,11? 因為bind改變了fooNewBind里面的this指向
5.JS常見的四種設計模式
5.1 工廠模式
簡單的工廠模式可以理解為解決多個相似的問題。
function CreatePerson(name,age,sex) {
? ? var obj = new Object();
? ? obj.name = name;
? ? obj.age = age;
? ? obj.sex = sex;
? ? obj.sayName = function(){? ?
? ? ? ? return this.name;
? ? }? ?
? ? return obj;
}
var p1 = new CreatePerson("longen",'28','男');
var p2 = new CreatePerson("tugenhua",'27','女');
console.log(p1.name); // longen
console.log(p1.age);? // 28
console.log(p1.sex);? // 男
console.log(p1.sayName()); // longen
console.log(p2.name);? // tugenhua
console.log(p2.age);? // 27
console.log(p2.sex);? // 女
console.log(p2.sayName()); // tugenhua?
5.2單例模式
只能被實例化(構造函數給實例添加屬性與方法)一次
// 單體模式
var Singleton = function(name){
? ? this.name = name;
};
Singleton.prototype.getName = function(){
? ? return this.name;
}
// 獲取實例對象
var getInstance = (function() {
? ? var instance = null;? ?
? ? return function(name) {? ?
? ? ? ? if(!instance) {//相當于一個一次性閥門,只能實例化一次
? ? ? ? ? ? instance = new Singleton(name);
? ? ? ? }? ? ? ?
? ? ? ? return instance;
? ? }
})();
// 測試單體模式的實例,所以a===b
var a = getInstance("aa");
var b = getInstance("bb");
5.3 沙箱模式
將一些函數放到自執行函數里面,但要用閉包暴露接口,用變量接收暴露的接口,再調用里面的值,否則無法使用里面的值。
let sandboxModel=(function(){? ?
? ? function sayName(){};? ?
? ? function sayAge(){};? ?
? ? return{? ?
? ? ? ? sayName:sayName,
? ? ? ? sayAge:sayAge
? ? }
})()
5.4 發布者訂閱模式
就例如如我們關注了某一個公眾號,然后他對應的有新的消息就會給你推送
//發布者與訂閱模式
? ? var shoeObj = {}; // 定義發布者
? ? shoeObj.list = []; // 緩存列表 存放訂閱者回調函數
? ? // 增加訂閱者
? ? shoeObj.listen = function(fn) {
? ? ? ? shoeObj.list.push(fn); // 訂閱消息添加到緩存列表
? ? }
? ? // 發布消息
? ? shoeObj.trigger = function() {
? ? ? ? ? for (var i = 0, fn; fn = this.list[i++];) {
? ? ? ? ? ? ? fn.apply(this, arguments);//第一個參數只是改變fn的this,
? ? ? ? ? ? }
? ? ? ? }
? ? // 小紅訂閱如下消息
? ? shoeObj.listen(function(color, size) {? ?
? ? ? ? console.log("顏色是:" + color);? ? ? ?
? ? ? ? console.log("尺碼是:" + size);
? ? });
? ? ? ?
? ? // 小花訂閱如下消息
? ? shoeObj.listen(function(color, size) {? ?
? ? ? ? console.log("再次打印顏色是:" + color);? ? ? ?
? ? ? ? console.log("再次打印尺碼是:" + size);
? ? });
? ? shoeObj.trigger("紅色", 40);
? ? shoeObj.trigger("黑色", 42);?
代碼實現邏輯是用數組存貯訂閱者, 發布者回調函數里面通知的方式是遍歷訂閱者數組,并將發布者內容傳入訂閱者數組。
6.原型鏈
6.1 定義
對象繼承屬性的一個鏈條
6.2構造函數,實例與原型對象的關系
var Person = function (name) { this.name = name; }//person是構造函數
var o3personTwo = new Person('personTwo')//personTwo是實例
原型對象都有一個默認的constructor屬性指向構造函數
6.3 創建實例的方法
1.字面量
let obj={'name':'張三'}
2.Object構造函數創建
let Obj=new Object()
Obj.name='張三'
3.使用工廠模式創建對象
function createPerson(name){
var o = new Object();
o.name = name;
};
return o;
}
var person1 = createPerson('張三');
4.使用構造函數創建對象
function Person(name){
this.name = name;
}
var person1 = new Person('張三');
6.4 new運算符
1.創了一個新對象;
2.this指向構造函數;
3.構造函數有返回,會替換new出來的對象,如果沒有就是new出來的對象
4.手動封裝一個new運算符
var new2 = function (func) {
? ? var o = Object.create(func.prototype); //創建對象
? ? var k = func.call(o); //改變this指向,把結果付給k
? ? if (typeof k === 'object') { //判斷k的類型是不是對象
? ? ? ? return k; //是,返回k
? ? } else {? ?
? ? ? ? return o; //不是返回返回構造函數的執行結果
? ? }
}?
6.5 對象的原型鏈
7.繼承的方式
JS是一門弱類型動態語言,封裝和繼承是他的兩大特性
7.1原型鏈繼承
將父類的實例作為子類的原型
1.代碼實現
定義父類:
// 定義一個動物類
function Animal (name) {
? // 屬性
? this.name = name || 'Animal';
? // 實例方法
? this.sleep = function(){?
? ? console.log(this.name + '正在睡覺!');
? }
}
// 原型方法
Animal.prototype.eat = function(food) {
? console.log(this.name + '正在吃:' + food);
};
子類:
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);//cat
console.log(cat.eat('fish'));//cat正在吃:fish? undefined
console.log(cat.sleep());//cat正在睡覺! undefined
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
2.優缺點
簡單易于實現,但是要想為子類新增屬性和方法,必須要在new Animal()這樣的語句之后執行,無法實現多繼承
7.2 構造繼承
實質是利用call來改變Cat中的this指向
1.代碼實現
子類:
function Cat(name){
? Animal.call(this);
? this.name = name || 'Tom';
}
2.優缺點
可以實現多繼承,不能繼承原型屬性/方法
7.3 實例繼承
為父類實例添加新特性,作為子類實例返回
1.代碼實現
子類
function Cat(name){
? var instance = new Animal();
? instance.name = name || 'Tom';
? return instance;
}
2.優缺點
不限制調用方式,但不能實現多繼承
7.4 拷貝繼承
將父類的屬性和方法拷貝一份到子類中
1.子類:
function Cat(name){
? var animal = new Animal();
? for(var p in animal){
? ? Cat.prototype[p] = animal[p];
? }
? Cat.prototype.name = name || 'Tom';
}
2.優缺點
支持多繼承,但是效率低占用內存
7.5 組合繼承
通過調用父類構造,繼承父類的屬性并保留傳參的優點,然后通過將父類實例作為子類原型,實現函數復用
1.子類:
function Cat(name){
? Animal.call(this);
? this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
7.6 寄生組合繼承
function Cat(name){
? Animal.call(this);
? this.name = name || 'Tom';
}
(function(){
? // 創建一個沒有實例方法的類
? var Super = function(){};
? Super.prototype = Animal.prototype;
? //將實例作為子類的原型
? Cat.prototype = new Super();
})();
7.7 ES6的extends繼承
ES6 的繼承機制是先創造父類的實例對象this(所以必須先調用super方法),然后再用子類的構造函數修改this,
class ColorPoint extends Point {
? constructor(x, y, color) {
? ? super(x, y); // 調用父類的constructor(x, y)
? ? this.color = color;
? }
? toString() {? ? return this.color + ' ' + super.toString(); // 調用父類的toString()
? }
}?