您好,登錄后才能下訂單哦!
一、代碼模塊
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);
閉包
1. 看一段閉包代碼
function a(){ var n = 0; function inc(){ n++; console.log(n); } inc(n); inc(n); } a(); //輸出1 輸出2
2.再 看一段閉包代碼
function a(){ var n = 0; this.inc = function(){ n++; console.log(n); }; } var c = new a(); c.inc(); //1 c.inc(); //2
3.閉包 有權訪問另一個函數作用域內的遍歷都是閉包, Inc函數訪問
構造函數a里面的遍歷n,所以形成一個閉包
4.匿名函數,lambda表達式,閉包區別:
從功能行上說他們是一個東西, 只是不同語言的不同稱呼罷了,
它們都是匿名函數, 若匿名函數捕獲了一個外部變量,它就是閉包.
qq交流群:140066160
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。