2018-06-27

JavaScript(面向對象+原型理解+繼承+作用域鏈和閉包+this使用總結)

一、面向對象

1、什么是面向對象

? 面向對象就是把構成問題事物分解成多個對象,建立對象不是為了完成某個步驟,而是描述某個事物在這個解決問題的步驟中的行為。

1.面向對象是一種思維方法

2.面向對象是一種編程方法

3.面向對象并不只針對某一種編程語言

2、面向對象和面向過程的區別和聯系

1.面向過程過程側重整個問題的解決步驟,著眼局部或者具體

2.面向對象側重具體的功能,讓某個對象具有這樣的功能。更加側重于整體。

各自的優缺點:
?
面向過程的優點:
流程化使得編程任務明確,在開發之前基本考慮實現的方法和最終結果;
效率高,面向過程強調代碼的短小精悍,善于結合數據結構來開發高效率程序;
流程明確,具體步驟清楚,便于節點分析。

  面向過程的缺點:
      需要深入的思考,耗費精力,代碼重用性低,擴展能力差,維護起來難度比較高,
      對復雜業務來說,面向過程的模塊難度較高,耦合度也比較高。

?
面向對象的優點:
結構清晰,程序便于模塊化,結構化,抽象化,更加符合人類的思維方式;
封裝性,將事務高度抽象,從而便于流程中的行為分析,也便于操作和自省;
容易擴展,代碼重用率高,可繼承,可覆蓋;
實現簡單,可有效地減少程序的維護工作量,軟件開發效率高。
?
面向對象的缺點是:
效率低,面向對象在面向過程的基礎上高度抽象,從而和代碼底層的直接交互非常少機會,從而不適合底層開發和游戲甚至多媒體開發;
復雜性,對于事務開發而言,事務本身是面向過程的,過度的封裝導致事務本身的復雜性提高。

3、面向對象的實現方式

? 面向對象的實現主流有兩種方式:基于類的面向對象和基于原型的面向對象。

? 面向對象三大特征:

● 封裝

也就是把客觀事物封裝成抽象的類或具體的對象,并且類或對象可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。

● 繼承

可以讓某個類型的對象獲得另一個類型的對象的屬性的方法

● 多肽

不同實例的相同方法在不同情形有不同表現形式。多態機制使具有不同內部結構的對象可以共享相同的外部接口。

3.1 基于類的面向對象

? 典型的語言:Java、C#

對象(object)依靠 類(class)來產生

3.2 基于原型的面向對象

? 典型的語言:JavaScript

對象(object)則是依靠 構造器(constructor)利用 原型(prototype)構造出來的

4、多種創建對象的方式

4.1 使用new Object()創建

4.2 工廠模式創建

? 工廠模式是軟件工程領域一種廣為人知的設計模式,這種模式抽象了創建具體對象的過程,考慮到在 ECMAScript 中無法創建類,開發人員就發明了一種函數,用函數來封裝以特定接口創建對象的細節。

4.3 構造函數模式創建

? 為了解決對象類型識別問題,又提出了構造函數模式。這種模式,其實在我們創建一些原生對象的時候,比如Array、Object都是調用的他們的構造函數。
<script type="text/javascript">
function Person (name, age, sex) {
this.name = name;

      this.age = age;
      this.sex = sex;
      
      this.eat = function () {
          alert(this.name + " Eat food");
      }
  }
  var p1 = new Person("Jack", 20, "man");
  p1.eat();   //Jack Eat food
  var p1 = new Person("Mark", 30, "man");
  p1.eat();   //Mark Eat food
  alert(p1 instanceof Person);    

</script>

5、構造函數與普通函數的關系

1.

他們都是函數。構造函數也是函數,也可以像普通的函數一樣進行調用。 做普通函數調用的時候,因為沒有創建新的對象,所以this其實指向了window對象。

function Person(){
this.name = "Jack"; // 把name屬性添加到了window對象上面
alert(this === window); //如果不作為構造方法調用,則 是true
}
Person(); // 把構造函數當做普通方法調用。這個時候內部的this指向了weindow
alert(window.name); //Jack
function Human(){
this.name = "Mark";
alert(this instanceof window); // false
alert(this instanceof Human); //true
}
var h = new Human(); //當做構造函數來調用,創建一個對象
alert(h.name);

2.

構造函數和普通函數僅僅也僅僅是調用方式的不同。也就是說,隨便一個函數你如果用new 的方式去使用,那么他就是一個構造函數。

3.

為了區別,如果一個函數想作為構造函數,作為國際慣例,最好把這個構造函數的首字母大寫。

4、閉包

<script type="text/javascript">
function createSumFunction(num1, num2){
return function () {
return num1 + num2;
};
}
var sumFun = createSumFunction(3, 4);
var sum = sumFun();
alert(sum);//7
</script>

在上面的代碼中,createSumFunction函數返回了一個匿名函數,而這個匿名函數使用了createSumFunction函數中的局部變量(參數),即使createSumFunction這個函數執行結束了,由于作用域鏈的存在,他的局部變量在匿名函數中仍然可以使用,這個匿名函數就是閉包。

? 閉包是指有權訪問另一個函數作用域中的變量的函數。

? 閉包是一種特殊的對象。它由兩部分構成: 函數,以及創建該函數的環境 。環境由閉包創建時在作用域中的任何局部變量組成。在我們的例子中,sumFun 是一個閉包,由 匿名 函數和閉包創建時存在的num1和num2 兩個局部變量組成。

5、閉包的應用

5.1 返回外部函數的局部變量

<script type="text/javascript">
function outer () {
var num = 5;
//定義一個內部函數
function inner () {
//內部函數的返回值是外部函數的一個局部變量
return num;
}
//把局部變量的值++
num++;
// 返回內部函數
return inner;
}
var num = outer()(); // 6
alert(num);
</script>

說明:

1.

這例子中,雖然函數的聲明在num++之前,但是函數返回的時候num已經++過了,所以只是num自增之后的值。

2.

結論:閉包中使用的局部變量的值,一定是局部變量的最后的值。

5.2 使用函數自執行和閉包封裝對象

封裝一個能夠增刪改查的對象

<script type="text/javascript">
var person = (function () {
//聲明一個對象,增刪改查均是針對這個對象
var personInfo = {
name : "李四",
age : 20
};
//返回一個對象,這個對象中封裝了一些對personInfor操作的方法
return {
//根據給定的屬性獲取這個屬性的值
getInfo : function (property) {
return personInfo[property];
},
//修改一個屬性值
modifyInfo : function (property, newValue) {
personInfo[property] = newValue;

          },
          //添加新的屬性
          addInfo : function (property, value) {
              personInfo[property] = value;
              
          },
           //刪除指定的屬性
          delInfo : function (property) {
              delete personInfo[property];
              
          }
      }
  })();
  alert(person.getInfo("name"));
  person.addInfo("sex", "男");
  alert(person.getInfo("sex"));

</script>

五、this使用總結

1、this使用總結

<script>
/*
* 默認綁定:
* 當直接調用一個函數的時候,就是默認綁定
* 1、非嚴格模式下,默認綁定到window上
*
* 隱式綁定:
* 當使用 對象.方法() 這種方式調用,稱之為隱式綁定
* this綁定到前面的那個對象上
*
* new 綁定:
* 使用new來調用構造函數的方式
* this是綁定在新創建的那個對象上
*
* 顯示綁定:
* call,apply:
* 都是一錘子買賣,僅僅這一次調用的時候使用了顯示綁定,對原函數沒有如何的影響
*
* call和apply的區別:就是參數的傳遞方式
* call:一個一個的傳遞
* apply:把要傳遞的參數封裝到一個數組中去傳遞
*
* bind:固定綁定 es6新增
* 調用函數對象的bind方法,返回一個固定this綁定的新的函數
* 對原來的函數沒有影響
*
*
*
* 優先級:bind > call,apply > new > 隱式
*/
?
?
//1、默認綁定
function foo() {
console.log(this);//this指 obj 誰調用指向誰
}
?
?
//2、隱式綁定
var name = "Mark";
var obj = {
name : "Jack",
foo : foo,
foo1 : function () {
console.log(this.name);//this指 window
}
}
obj.foo();//{name: "Jack", foo: function, foo1: function}
var foo1 = obj.foo1;
foo1();//Mark
?
?
//3、new 綁定
function foo2() {
this.name = "Lucy";
console.log(this);//this 指向foo2 foo2 {name: "Lucy"}
}
var obj2 = new foo2();
console.log(obj2);//this 指向foo2 foo2 {name: "Lucy"}
?
var foo3 = obj.foo1;
foo3();//Mark
?
var obj3 = new foo;//foo {}
console.log(obj3);//foo {}
?
?
//4、顯示綁定
?
//call apply
function foo4(a,b) {
console.log(this.name,a,b);
}
foo4.call({name:"Jack"},10,20)//Jack 10 20
foo4.apply({name:"Mark"},[20,10])//Mark 20 10
?
//apply求max
var arr = [23,65,2,45,3,57,4567];
console.log(Math.max.apply(Math,arr)); //max = 4567
?
//定義Math 求和的方法
Math.sun = function () {
return Array.prototype.reduce.call(arguments,function (sun,ele) {
return sun + ele;
},0)
}
console.log(Math.sun.apply(Math,arr));//sum = 4762
?
//bind
var obj4 = {
name : "Jack"
}
function foo5() {
console.log(this.name);
}
var f = foo5.bind(obj4);//這種綁定方式優先級最高
f();//Jack
?
var obj5 = {
name : "Mark",
foo6 : f
}
obj5.foo6()//Jack
</script>

2、綁定的丟失問題
<script>
/*
* 回調函數的this綁定丟失問題:this會綁定到window
*
* 定時器
*
*
* 顯示綁定丟失問題
* 顯示綁定傳入null、undefined時,this就成了默認綁定
*
*/
?
//定時器綁定丟失
var name = "Jack";
var obj = {
name : "Mark",
show : function () {
setInterval(function () {
console.log(this.name);// 此時this指向window
},1000)
}
}
obj.show();//Jack++
?
//解決方法一
var obj1 = {
name : "Mark",
show : function () {
var self = this;
setInterval(function () {
console.log(self.name);// 此時this指向obj1
},1000)
}
}
obj1.show();//Mark++
?
//解決方法二
var obj2 = {
name : "Joe",
show : function () {
setInterval(function () {
console.log(this.name);// 此時this指向obj2
}.bind(this),1000)
}
}
obj2.show();//Mark++
?
//顯示綁定丟失
function foo() {
console.log(this.name);
}
var f = foo.bind(undefined);// 此時this指向window
f();//Jack
</script>

五、this使用總結

1、this使用總結

<script>
/*
* 默認綁定:
* 當直接調用一個函數的時候,就是默認綁定
* 1、非嚴格模式下,默認綁定到window上
*
* 隱式綁定:
* 當使用 對象.方法() 這種方式調用,稱之為隱式綁定
* this綁定到前面的那個對象上
*
* new 綁定:
* 使用new來調用構造函數的方式
* this是綁定在新創建的那個對象上
*
* 顯示綁定:
* call,apply:
* 都是一錘子買賣,僅僅這一次調用的時候使用了顯示綁定,對原函數沒有如何的影響
*
* call和apply的區別:就是參數的傳遞方式
* call:一個一個的傳遞
* apply:把要傳遞的參數封裝到一個數組中去傳遞
*
* bind:固定綁定 es6新增
* 調用函數對象的bind方法,返回一個固定this綁定的新的函數
* 對原來的函數沒有影響
*
*
*
* 優先級:bind > call,apply > new > 隱式
*/
?
?
//1、默認綁定
function foo() {
console.log(this);//this指 obj 誰調用指向誰
}
?
?
//2、隱式綁定
var name = "Mark";
var obj = {
name : "Jack",
foo : foo,
foo1 : function () {
console.log(this.name);//this指 window
}
}
obj.foo();//{name: "Jack", foo: function, foo1: function}
var foo1 = obj.foo1;
foo1();//Mark
?
?
//3、new 綁定
function foo2() {
this.name = "Lucy";
console.log(this);//this 指向foo2 foo2 {name: "Lucy"}
}
var obj2 = new foo2();
console.log(obj2);//this 指向foo2 foo2 {name: "Lucy"}
?
var foo3 = obj.foo1;
foo3();//Mark
?
var obj3 = new foo;//foo {}
console.log(obj3);//foo {}
?
?
//4、顯示綁定
?
//call apply
function foo4(a,b) {
console.log(this.name,a,b);
}
foo4.call({name:"Jack"},10,20)//Jack 10 20
foo4.apply({name:"Mark"},[20,10])//Mark 20 10
?
//apply求max
var arr = [23,65,2,45,3,57,4567];
console.log(Math.max.apply(Math,arr)); //max = 4567
?
//定義Math 求和的方法
Math.sun = function () {
return Array.prototype.reduce.call(arguments,function (sun,ele) {
return sun + ele;
},0)
}
console.log(Math.sun.apply(Math,arr));//sum = 4762
?
//bind
var obj4 = {
name : "Jack"
}
function foo5() {
console.log(this.name);
}
var f = foo5.bind(obj4);//這種綁定方式優先級最高
f();//Jack
?
var obj5 = {
name : "Mark",
foo6 : f
}
obj5.foo6()//Jack
</script>

2、綁定的丟失問題
<script>
/*
* 回調函數的this綁定丟失問題:this會綁定到window
*
* 定時器
*
*
* 顯示綁定丟失問題
* 顯示綁定傳入null、undefined時,this就成了默認綁定
*
*/
?
//定時器綁定丟失
var name = "Jack";
var obj = {
name : "Mark",
show : function () {
setInterval(function () {
console.log(this.name);// 此時this指向window
},1000)
}
}
obj.show();//Jack++
?
//解決方法一
var obj1 = {
name : "Mark",
show : function () {
var self = this;
setInterval(function () {
console.log(self.name);// 此時this指向obj1
},1000)
}
}
obj1.show();//Mark++
?
//解決方法二
var obj2 = {
name : "Joe",
show : function () {
setInterval(function () {
console.log(this.name);// 此時this指向obj2
}.bind(this),1000)
}
}
obj2.show();//Mark++
?
//顯示綁定丟失
function foo() {
console.log(this.name);
}
var f = foo.bind(undefined);// 此時this指向window
f();//Jack
</script>

五、this使用總結

1、this使用總結

<script>
/*
* 默認綁定:
* 當直接調用一個函數的時候,就是默認綁定
* 1、非嚴格模式下,默認綁定到window上
*
* 隱式綁定:
* 當使用 對象.方法() 這種方式調用,稱之為隱式綁定
* this綁定到前面的那個對象上
*
* new 綁定:
* 使用new來調用構造函數的方式
* this是綁定在新創建的那個對象上
*
* 顯示綁定:
* call,apply:
* 都是一錘子買賣,僅僅這一次調用的時候使用了顯示綁定,對原函數沒有如何的影響
*
* call和apply的區別:就是參數的傳遞方式
* call:一個一個的傳遞
* apply:把要傳遞的參數封裝到一個數組中去傳遞
*
* bind:固定綁定 es6新增
* 調用函數對象的bind方法,返回一個固定this綁定的新的函數
* 對原來的函數沒有影響
*
*
*
* 優先級:bind > call,apply > new > 隱式
*/
?
?
//1、默認綁定
function foo() {
console.log(this);//this指 obj 誰調用指向誰
}
?
?
//2、隱式綁定
var name = "Mark";
var obj = {
name : "Jack",
foo : foo,
foo1 : function () {
console.log(this.name);//this指 window
}
}
obj.foo();//{name: "Jack", foo: function, foo1: function}
var foo1 = obj.foo1;
foo1();//Mark
?
?
//3、new 綁定
function foo2() {
this.name = "Lucy";
console.log(this);//this 指向foo2 foo2 {name: "Lucy"}
}
var obj2 = new foo2();
console.log(obj2);//this 指向foo2 foo2 {name: "Lucy"}
?
var foo3 = obj.foo1;
foo3();//Mark
?
var obj3 = new foo;//foo {}
console.log(obj3);//foo {}
?
?
//4、顯示綁定
?
//call apply
function foo4(a,b) {
console.log(this.name,a,b);
}
foo4.call({name:"Jack"},10,20)//Jack 10 20
foo4.apply({name:"Mark"},[20,10])//Mark 20 10
?
//apply求max
var arr = [23,65,2,45,3,57,4567];
console.log(Math.max.apply(Math,arr)); //max = 4567
?
//定義Math 求和的方法
Math.sun = function () {
return Array.prototype.reduce.call(arguments,function (sun,ele) {
return sun + ele;
},0)
}
console.log(Math.sun.apply(Math,arr));//sum = 4762
?
//bind
var obj4 = {
name : "Jack"
}
function foo5() {
console.log(this.name);
}
var f = foo5.bind(obj4);//這種綁定方式優先級最高
f();//Jack
?
var obj5 = {
name : "Mark",
foo6 : f
}
obj5.foo6()//Jack
</script>

2、綁定的丟失問題
<script>
/*
* 回調函數的this綁定丟失問題:this會綁定到window
*
* 定時器
*
*
* 顯示綁定丟失問題
* 顯示綁定傳入null、undefined時,this就成了默認綁定
*
*/
?
//定時器綁定丟失
var name = "Jack";
var obj = {
name : "Mark",
show : function () {
setInterval(function () {
console.log(this.name);// 此時this指向window
},1000)
}
}
obj.show();//Jack++
?
//解決方法一
var obj1 = {
name : "Mark",
show : function () {
var self = this;
setInterval(function () {
console.log(self.name);// 此時this指向obj1
},1000)
}
}
obj1.show();//Mark++
?
//解決方法二
var obj2 = {
name : "Joe",
show : function () {
setInterval(function () {
console.log(this.name);// 此時this指向obj2
}.bind(this),1000)
}
}
obj2.show();//Mark++
?
//顯示綁定丟失
function foo() {
console.log(this.name);
}
var f = foo.bind(undefined);// 此時this指向window
f();//Jack
</script>

五、this使用總結

1、this使用總結

<script>
/*
* 默認綁定:
* 當直接調用一個函數的時候,就是默認綁定
* 1、非嚴格模式下,默認綁定到window上
*
* 隱式綁定:
* 當使用 對象.方法() 這種方式調用,稱之為隱式綁定
* this綁定到前面的那個對象上
*
* new 綁定:
* 使用new來調用構造函數的方式
* this是綁定在新創建的那個對象上
*
* 顯示綁定:
* call,apply:
* 都是一錘子買賣,僅僅這一次調用的時候使用了顯示綁定,對原函數沒有如何的影響
*
* call和apply的區別:就是參數的傳遞方式
* call:一個一個的傳遞
* apply:把要傳遞的參數封裝到一個數組中去傳遞
*
* bind:固定綁定 es6新增
* 調用函數對象的bind方法,返回一個固定this綁定的新的函數
* 對原來的函數沒有影響
*
*
*
* 優先級:bind > call,apply > new > 隱式
*/
?
?
# //1、默認綁定
function foo() {
console.log(this);//this指 obj 誰調用指向誰
}
?
?
#//2、隱式綁定
var name = "Mark";
var obj = {
name : "Jack",
foo : foo,
foo1 : function () {
console.log(this.name);//this指 window
}
}
obj.foo();//{name: "Jack", foo: function, foo1: function}
var foo1 = obj.foo1;
foo1();//Mark
?
?
# //3、new 綁定
function foo2() {
this.name = "Lucy";
console.log(this);//this 指向foo2 foo2 {name: "Lucy"}
}
var obj2 = new foo2();
console.log(obj2);//this 指向foo2 foo2 {name: "Lucy"}
?
var foo3 = obj.foo1;
foo3();//Mark
?
var obj3 = new foo;//foo {}
console.log(obj3);//foo {}
?
?
# //4、顯示綁定
?
//call apply
function foo4(a,b) {
console.log(this.name,a,b);
}
foo4.call({name:"Jack"},10,20)//Jack 10 20
foo4.apply({name:"Mark"},[20,10])//Mark 20 10
?
//apply求max
var arr = [23,65,2,45,3,57,4567];
console.log(Math.max.apply(Math,arr)); //max = 4567
?
//定義Math 求和的方法
Math.sun = function () {
return Array.prototype.reduce.call(arguments,function (sun,ele) {
return sun + ele;
},0)
}
console.log(Math.sun.apply(Math,arr));//sum = 4762
?
//bind
var obj4 = {
name : "Jack"
}
function foo5() {
console.log(this.name);
}
var f = foo5.bind(obj4);//這種綁定方式優先級最高
f();//Jack
?
var obj5 = {
name : "Mark",
foo6 : f
}
obj5.foo6()//Jack
</script>

2、綁定的丟失問題

<script>
/*
* 回調函數的this綁定丟失問題:this會綁定到window
*
* 定時器
*
*
* 顯示綁定丟失問題
* 顯示綁定傳入null、undefined時,this就成了默認綁定
*
*/
?
//定時器綁定丟失
var name = "Jack";
var obj = {
name : "Mark",
show : function () {
setInterval(function () {
console.log(this.name);// 此時this指向window
},1000)
}
}
obj.show();//Jack++
?
//解決方法一
var obj1 = {
name : "Mark",
show : function () {
var self = this;
setInterval(function () {
console.log(self.name);// 此時this指向obj1
},1000)
}
}
obj1.show();//Mark++
?
//解決方法二
var obj2 = {
name : "Joe",
show : function () {
setInterval(function () {
console.log(this.name);// 此時this指向obj2
}.bind(this),1000)
}
}
obj2.show();//Mark++
?
//顯示綁定丟失
function foo() {
console.log(this.name);
}
var f = foo.bind(undefined);// 此時this指向window
f();//Jack
</script>

5.3 for循環典型問題

看下面的代碼

<body>
<input type="button" value="按鈕1" >
<input type="button" value="按鈕2" >
<input type="button" value="按鈕3" >
<script type="text/javascript">
var btns = document.getElementsByTagName("input");
for (var i = 0; i < 3; i++) {
btns[i].onclick = function () {
alert("我是第" + (i + 1) + "個按鈕");
};
}
</script>
</body>

發現在點擊三個按鈕的時候都是彈出 我是第4個按鈕。 為什么呢?閉包導致的! 每循環一次都會有一個匿名函數設置點擊事件,閉包總是保持的變量的最后一個值,所以點擊的時候,總是讀的是 i 的組后一個值4.

解決方案1:給每個按鈕添加一個屬性,來保存 每次 i 的臨時值

<body>
<input type="button" value="按鈕1" >
<input type="button" value="按鈕2" >
<input type="button" value="按鈕3" >
<script type="text/javascript">
var btns = document.getElementsByTagName("input");
for (var i = 0; i < 3; i++) {
//把i的值綁定到按鈕的一個屬性上,那么以后i的值就和index的值沒有關系了。
btns[i].index = i;
btns[i].onclick = function () {
alert("我是第" + (this.index + 1) + "個按鈕");
};
}
</script>
</body>

解決方案2:使用匿名函數的自執行

<body>
<input type="button" value="按鈕1" >
<input type="button" value="按鈕2" >
<input type="button" value="按鈕3" >
<script type="text/javascript">
var btns = document.getElementsByTagName("input");
for (var i = 0; i < 3; i++) {
//因為匿名函數已經執行了,所以會把 i 的值傳入到num中,注意是i的值,所以num
(function (num) {
btns[i].onclick = function () {
alert("我是第" + (num + 1) + "個按鈕");
}
})(i);
}
</script>
</body>

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

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,868評論 18 139
  • 一、你不知道的JavaScript 1、作用域 作用域 LHS RHS RHS查詢與簡單地查找某個變量的值別無二...
    頂兒響叮當閱讀 351評論 0 0
  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執行單位為行(line),也就是一...
    悟名先生閱讀 4,195評論 0 13
  • 如何控制alert中的換行?\n alert(“p\np”); 請編寫一個JavaScript函數 parseQu...
    heyunqiang99閱讀 1,101評論 0 6
  • 1、使用typeof bar ===“object”來確定bar是否是一個對象時有什么潛在的缺陷?這個陷阱如何避免...
    深海鯽魚堡閱讀 668評論 1 1