1. 程式人生 > >JavaScript高級部分

JavaScript高級部分

javascript node.js

一、代碼模塊

1.js裏面代碼可以放在不同的文件裏, 稱為模塊

2.一個模塊需要引用其他模塊代碼的時候,使用require()

3.require:

(1)如果是第一次調用,那麽就加載,執行腳本

(2)每個代碼模塊由module.exports導出的對象

(3)每次require的時候,都返回module.exports(模塊出口)

他可以指向任何類型, 函數,對象, 值.....

4.requirefunction Person(name,height){
this.name=name;
this.height=height;
this.hobby=function(){
return 'watching movies';

}
}

var boy=new Person('keith',180);
var girl=new Person('rascal',153);
console.log(boy.name); //'keith'
console.log(girl.name); //'rascal'
console.log(boy.hobby===girl.hobby);//false

, 如果是第一次執行,他就把

js裏所有代碼加載進來, 在執行這整個js文件, 然後返回module.exports.

require得到的就是module.exports指向的對象.

5.如果不是第一次require,他就直接返回module.exports.






二、this


顯示傳遞this call


1.每個函數都包含兩個非繼承而來的方法:call()方法和apply()方法。

(1)相同點: 兩個方法的作用是一樣的

(2)不同點: 接收參數的方式不同.

2.作用:

.在特定的作用於中調用函數,等於設置函數體內this對象的值,

擴充函數賴以運行的作用域

(1)call方法使用

function test_func(name,sex){
    this.name = name;
    this.sex = sex;
}

var xiaoming = {};
test_func.call(xiaoming func,"xiaoming",10);
console.log(xiaoming);

(2)apply使用 兩個參數 一個是函數的作用域(this),另一個是參數數組.

function test_func(name,sex){
    this.name = name;
    this.sex = sex;
}

var xiaoming = {};
test_func.apply(xiaoming,["xiaoming",10]);
console.log(xiaoming);



3.隱式傳遞this

這個this就是對象, 也就是對象直接調用,this被隱式傳到函數裏,

var xiaohong = {
    name: "xiaohong",
    test_func: function(){
        console.log(this);
    },
}
xiaohong.test_func();




4.強制傳遞綁定this

強制設置函數體this的值, 參數是一個表.


但是對象卻不會引用這個函數>

var func = function(){
    console.log(this);
}.bind(xiaohong);
func();



5.定義完函數體,在綁定對象。

在定義完函數體後, 這個函數對象的this一定不是綁定的;

因為,函數對象來綁定對象的時候,不是把對象綁定到func函數對象裏,

而是產生了一個新的函數對象,然後返回這個函數對象.

(1)錯誤,沒有獲得綁定好的函數對象

func = function(){
    console.log(this);
}
func.bind(xiaohong);


(2)正確,先獲得綁定好的函數對象,再調用

func = function(){
    console.log(this);
}
func = func.bind(xiaohong);
func();



7.優先級

(1)綁定的優先級 大於 隱式傳遞

綁定一般用在回調函數。

var xiaohong = {
    name: "xiaohong",
    test_func: function(){
        console.log(this);
    }.bind(5),
}
xiaohong.test_func(); //輸出的是5


(2)顯示傳遞 大於 隱式傳遞

強制 > 顯示 > 隱式








三、實現面向對象

.在javascript中不存在類的概念, 而是通過 構造函數

和原型鏈(prototype chains)實現的

function person(name, sex){
    this.name = name;
    this.sex = sex;    
}

person.prototype.test_func = function(){
    console.log("person test_func");
    console.log(this);
}

var p = new person("xiaohong",10);
console.log(p);


var p2 = new person("xiaotian",12);
console.log(p2);

p.test_func();
p2.test_func();





構造函數
(1)構造函數提供一個生成對象的模板並描述對象的基本結構,

一個構造函數可以生成多個對象,每個對象都有相同的結構,

構造函數就是對象的模板, 對象就是構造函數的實例,

構造函數特點:

a:內部使用的this對象,指向要生成的對象實例,

b:使用new操作符來調用構造函數,並返回對象實例,



(2)構造函數的缺點: 同一個對象實例之間, 當你new調用構造函數

返回一個實例的時候,裏面的所有函數都會新創建出來, 這個函數

都是一個新創建的函數, 是不被實例共享的, 這樣很浪費資源.

function Person(name,height){
 this.name=name;
 this.height=height;
 this.hobby=function(){
 return 'watching movies';
}
}

var boy=new Person('keith',180);
 var girl=new Person('rascal',153); 
 console.log(boy.name); //'keith'
 console.log(girl.name); //'rascal'
 console.log(boy.hobby===girl.hobby);//false




prototype屬性

(1) 為了解決構造函數的對象之間無法共享屬性的缺點,js提供了prototype屬性.


(2)js中所有類型都是對象,(除了null和undefined),而每個對象都繼承自另一個

對象, 稱為"原型"對象 (prototype object) , null沒有原型對象,


(3)原型對象上的所有屬性和方法,都會被對象實例所享.


(4)通過構造函數生成對象實例時, 會將對象實例的原型指向構造函數

的prototype屬性, 每一個構造函都有一個prototype屬性,

這個屬性就是對象實例的原型對象,


(5)對於構造函數prototype是作為構造函數的屬性,對於對象實例來說

prototype是對象實例的原型對象, 所以prorotype即是屬性,又是對象.

function Person(name,height){
 this.name=name;
 this.height=height;

}

 Person.prototype.hobby = function(){
     return 'watching movies';
}

var boy = new Person('keith',180);
 var girl = new Person('rascal',153); 
 console.log(boy.name); //'keith'
 console.log(girl.name); //'rascal'
 console.log(boy.hobby===girl.hobby);//true

(6)原型對象的屬性不是對象實例的屬性,對象實例的屬性是繼承自

構造函數定義的屬性,因為構造函數內部有一個this關鍵字,來指向

要生成的對象實例,對象實例屬性, 其實就是構造函數內部定義的屬性.

所以只要你修改原型對象上的屬性和方法, 變動會離開體現在所有對象實例上.


(7)當某個對象實例沒有該屬性或方法時, 就會到原型對象上去查找

如果實例對象自身有某個對象或方法, 就不會去原型對象上查找,


如下,當boy對象實例hobby方法修改時, 就不會在繼承原型對象

上的hobby方法了, 不會girl沒有修改, 所以它依舊繼承原型對象的方法.

function Person(name,height){
 this.name=name;
 this.height=height;

}
 Person.prototype.hobby = function(){
     console.log("aaa");
} 
var boy = new Person('keith',180);
var girl = new Person('rascal',153);  
boy.hobby = function(){
    console.log("bbb");
}

boy.hobby(); //bbb
girl.hobby(); //aaa


(8).原型鏈

對象的屬性和方法,可能是定義在自身,也可能是原型對象,由於

原型對象本身對於對象實例來說也是對象,它也有自己的原型, 所以形成了

一條原型鏈(prototype chain),比如a對象是b對象的原型,b對象是c對象的原型

以此類推, 所有對象的原型頂端, 都是object.prototype,即object構造

函數的prototype指向的哪個對象, 而object.prototype也有自己的原型對象,

這個對象就是 "null" 沒有任何屬性和方法, 而null對象則沒有原型.

特點:

a.讀取對象某個屬性時,javaScript先找對象本身的屬性,如果找不到,就去他的原型找,

如果還是找不到,則去原型的原型找,直到object.portotype找不到則返回 undefined.

b.如果對象自身和它的原型定義了同名屬性,優先讀取對象自身屬性,稱為 "覆蓋"

c.一級級向上找屬性, 對性能有影響, 越上層,影響越大,



new 操作符

1.javascript也有new關鍵字, js中萬物皆對象, 為何還要new關鍵字.

其實js中new關鍵字不是用來創建一個類的實例對象,而是用於繼承,

function Animal(name){
  this.name = name;
}
Animal.color = "black";
Animal.prototype.say = function(){
  console.log("I'm " + this.name);
};
var cat = new Animal("cat");

console.log(
  cat.name, //cat
  cat.height //undefined
);
cat.say(); //I'm cat

console.log(
  Animal.name, //Animal
  Animal.color //back
);
Animal.say(); //not function

2.代碼解讀

1-3 行創建一個函數Animal,並在其this上定義了屬性:name,

4 行在Animal對象(Animal本身是函數對象)上定義了一個靜態屬性:color,並復制"black"

5-7行在Animal函數的屬性prototype上定義一個say方法,say輸出了this的name值

8行使用new關鍵字創建了一個新對象cat,

10-14行cat對象常識方位name和color屬性,並調用say方法

16-20行Animal對象訪問name和color屬性,並調用say方法



3.重點解析 new方法做了什麽?

js引擎在執行new 這行代碼的時候,做了很懂工作,偽代碼如下

var obj = {};
obj.__proto__ = Animal.prototype;
var result = Animal.call(obj,"cat"); //顯示傳遞this,和參數到構造函數
return typeof result === 'obj'? result : obj ;  //

a)__proto__(隱式原型)與prototype(顯式原型)

每個對象都有__proto__屬性, 但只有函數對象才有prototype屬性.

b ) 首先會創建一個空對象obj, 把obj的__proto__指向Animal的原型對象

prototype, 此時便簡歷了obj對象的原型鏈:

obj -> Animal.prototype -> object.prototype -> null

c)在obj對象執行空間調用構造函數並傳遞參數"cat"

這裏這個obj就是構造函數裏的this對象,this指向的就是obj;

d ) 判斷返回值 如果無返回值或者返回非對象值, 則將obj作為新對象, 否則返回值

坐位新對象,



4.了解new運行機制後,cat就是d過程的返回值 表, 他有一個原型鏈,

cat上新增了一個新的屬性: name


5.再次參照上面的代碼分析

(1)cat.naem 在過程c 中obj作為this,傳遞到構造函數,構造產生name屬性,

因此cat.name就是obj.name

(2)cat.color cat首先在自身找color,沒有則言責原型鏈查找,我們僅在Animal

對象是哪個定義color,沒有再其原型鏈上定義, 因此找不到

(3)cat.say() 首先找自身,沒有,找原型鏈Animal的prototype屬性定義了say

因此可以再原型鏈是上找到say方法

(4)Animal.color 對於Animal來說它本身是一個對象,因此他在訪問屬性也遵守

上述查找規則, 所以他能找到

(5)Animal.name 先查找自身naem 但這個name不是我們定義的name,

而是這個函數對象內部本身就存在這個屬性,一般情況下,函數對象在產生內置name

屬性會將函數名作為賦值(僅函數對象).

(6)Animal.say() Animal在自身查找, 沒有,沿著原型鏈查找,

這是Animal函數對象的原型鏈

Animal的原型對象是Function.prototype

原型鏈: Animal -> Function.prototype -> Objecct.prototype -> null

技術分享圖片

在Animal原型鏈上並沒有找到,say方法, 因為Animal的prototype

只是Animal的一個屬性, 並不是它的原型對象,

Animal的原型對象是Function.prototype

(7)所有實例化的對象,他們的__protp__都指向構造函數的prototype屬性

只要有一個實例對象修改其中的內容, 其他的實例對象也會跟這改變.

(8)根據上面的知識實現面向對象

function Point(){
    this.xpos = 0;
    this.ypos = 0;
}
Point.prototype.set_pos = function(x,y){
    this.xpos = x;
    this.ypos = y;
}

Point.prototype.get_posy = function(){
    return this.ypos;
}

var p1 = new Point();
var p2 = new Point();
p1.set_pos(10,20);
p2.set_pos(100,200);
console.log(p1.get_posy());
console.log(p2.get_posy());




類的繼承(原型實現)

1.繼承就是父類有的方法或屬性, 子類繼承後其方法和屬性子類也可以使用的.

2.繼承實現方法: A類有個prototype, 類B也有個prototype

把類A的prototype的內容 復制(淺復制) 給類B的prototype,

這樣的話,類A和類B公用了這個prototype

3.首先定義一個人類

var Person = function(){};
Person.prototype.set_name = function(name){
    this.naem = name; //設置姓名
};
Person.prototype.set_age = function(age){
    this.age = age; //設置年齡
};

4.定義一個男人類, 繼承自人類, 有兩種做法

(1)男人類.prototype = Person.prototype;

但是這樣會造成一個問題:這樣賦值就是引用賦值 (淺賦值),

導致這兩個prototype指向的是同一個對象, 如果子類修改一個

方法屬性, 父類的也會跟著改變, 顯然是不行的.


(2)new出來的是新的實例,會把原來的prototype拷貝過來

這樣就可以擴展自己的方法了, 實際上這個prototype的__ptoto還是

指向父類的prototype, 而prototype則共享__ptoto__裏的屬性方法,

擴展子類prototype的時候,不影響父類ptototype, 應為是new出來的, 他的

__proto__才指向父類prototype, ptototype只是共享這個方法屬性.

//定義一男人類
var Man = function(){};
//做法(1)

//做法(2)
var Super = function(){};
Super.prototype = Person.prototype;
Man.prototype = new Super();
//這裏就是繼承
Man.prototype.set_sex = function(sex){
    this.sex = sex;
}

console.log(Super.prototype);
console.log(Man.prototype);
console.log(Man.prototype.__proto__);
var m = new Man();
//使用父類方法
m.set_name("小王");
m.set_age(10);
//使用子類方法
m.set_sex(0);
console.log(m);


(3) 只要你擴展自己方法,和父類同名,就會隱藏父類方法,

因給根據原型鏈原則, 查找方法屬性 先到實例本身進行查找 然後原型對象

這個原型其實就是他的父類.

Man.prototype.set_name = function(name){
    console.log("子類重寫該方法");
}

(4) 如果要調用父類的函數, 使用父類的ptototype 把子類實例傳給父類方法

Man.prototype.set_name = function(name){
    person.prototype.set_name.call(name);
    console.log("子類重寫該方法");
}



(5)Class實現 可以繼承 也可以繼承

function Class(param){
    var new_class = function(){};
    //如果有要繼承的父類
    if(param.extend != null){
        var Super = function(){};
        Super.prototype = param.extend.prototype;
        new_class.prototype = new Super();
    }
    //遍歷參數 把內容添加到新對象裏
    for(var key in param){
        if(key == "extend"){
            continue;
        }
        new_class.prototype[key] = param[key];
    }

    return new_class;
}


(6)使用Class函數 實現繼承和 擴展方法屬性

//Student學生類 傳入參數表
var Student = Class({
    //繼承自 Person
    extend: Person,

    //定義方法
    set_class: function(classa){
        console.log("set_class",classa);
    },

    //定義屬性
    name: "學生類",
});

//實例化對象
var s = new Student();
s.set_class(12312);
console.log(s.name);


qq交流群:140066160




JavaScript高級部分