前言
javaScritp誕生于95年當(dāng)時工作在Netscape(網(wǎng)景)公司的布蘭登(Brendan Eich)為解決類似于“向服務(wù)器提交數(shù)據(jù)之前驗證”的問題贸宏。在Netscape Navigator 2.0與Sun公司聯(lián)手開發(fā)一個稱之為LiveScript的腳本語言看成,經(jīng)過發(fā)展启涯,它成為一種面向?qū)ο笮偷慕忉屝偷恼Z言。也是基于對象和事件驅(qū)動具有相對安全的性的客戶端(瀏覽器)腳本語言。主要目的為了驗證發(fā)往服務(wù)器端的數(shù)據(jù),增加web互動,加強(qiáng)用戶體驗度击儡。也有類似java封裝繼承的特點。
內(nèi)置對象
- Global沒有方法可以直接訪問蝠引。web瀏覽器通常把它當(dāng)作window對象阳谍,加以部分實現(xiàn) 并有undefined、NaN螃概、Object矫夯、Array、Function屬性
- encodeURIComponent()的編碼方法
alert(decodeURIComponent(encodeURIComponent('//wang王')))
- eval() 強(qiáng)大又危險的字符串解析器
eval('var box = 100');//解析了字符串代碼吊洼,同樣可以注入是危險
alert(box);
eval('alert(100)');
eval('function box() {return 123}');//函數(shù)也可以
- data對象
//獲取當(dāng)前時間
var myDate = new Date();
var mytime=myDate.toLocaleTimeString(); //獲取當(dāng)前時間
myDate.toLocaleDateString(); //獲取當(dāng)前日期
myDate.toLocaleString( ); //獲取日期與時間
- math對象 提供更快的計算操作
- min()和max() 獲取一組數(shù)中的最小最大值
alert(Math.min(2,4,3,6,3,8,0,1,3));//最小值
alert(Math.max(4,7,8,3,1,9,6,0,3,2));//最大值
- Math.ceil(),Math.floor(),Math.round() 三種向上训貌,向下,標(biāo)準(zhǔn)舍入
alert(Math.ceil(25.1)); //26
alert(Math.floor(25.9)); //25
alert(Math.round(25.9)); //26
alert(Math.round(25.5)); //26
alert(Math.round(25.1)); //25
- random()獲取隨機(jī)數(shù)方法
//獲取指定范圍的隨機(jī)數(shù)
function getRandom(numStart,numEnd) {
return Math.floor(Math.random()*((numEnd-numStart)+1)+numStart) ;
}
//獲取指定長度隨機(jī)字符串
function getRandomStr(len) {
len = len || 4;
var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; //去掉易混淆的字符
var maxPos = chars.length;
var random = '';
for(var i = 0; i<len; i++){
random += chars.charAt(Math.floor(Math.random()*maxPos))
}
return random;
}
- Math.abs(num) 返回num的絕對值
js中的this關(guān)鍵字和new 關(guān)鍵字
- js中沒有塊級作用域只有函數(shù)作用域冒窍,例如if,while,for代碼塊递沪。如果沒有通過var 聲明屬于window對象
function varscope(){
foo = "I'm in function";
console.log(foo);//I'm in function
}
varscope();
console.log(window.foo); //I'm in function 屬于window對象
var obj={name:"hello",show:function(){
console.log(this.name);
}};
obj.show(); //hellow
var objA={name:"world"}
objA.show2=obj.show; //在調(diào)用的時候這個相當(dāng)于把this 指向了方法ObjA,而不是obj。
objA.show() //word
使用 apply(作用域综液,參數(shù)數(shù)組)和call(作用域倘待,參數(shù)列表) 改變this的指向(函數(shù)的執(zhí)行環(huán)境)實現(xiàn)對象冒充和繼承高氮。每個函數(shù)都包含這兩個非繼承的方法皮获。
//聲明兩個全局變量
window.firstName = "my";
window.lastName = "_obj";
//聲明一個內(nèi)部變量
var myObject = {firstName:'my', lastName:'test'};
//聲明兩個獲取屬性的方法
function getName(){
console.log(this.firstName + this.lastName);
}
function getMessage(sex,age){
console.log(this.firstName + this.lastName + " 性別: " + sex + " age: " + age );
}
//測試
getName.call(window); // my_obj
getName.call(myObject); // mytest
getName.apply(window); // my_obj
getName.apply(myObject);// mytest
getMessage.call(window,"女",21); //my_obj 性別: 女 age: 21
getMessage.apply(window,["女",21]); // my_obj 性別: 女 age: 21
getMessage.call(myObject,"未知",22); //mytest性別: 未知 age: 22
getMessage.apply(myObject,["未知",22]); // mytest性別: 未知 age: 22
//改變對象的作用域惫企,把數(shù)據(jù)傳遞出去逐哈。
function mouseClick(A,fun) { //A 為插件书释,fun為函數(shù)類型
var data = {}; //創(chuàng)建一個MouseClick的對象
A.plugin(' ',function(){ //有些數(shù)據(jù)的處理可能必須在工具/插件內(nèi)部作用域進(jìn)行
//完成業(yè)務(wù)邏輯處理
....
....
fun.call(this,data); // 改變data的this指向
});
}
//調(diào)用
mouseClick(a,function (e) { //或者使用this得到data數(shù)據(jù)
//進(jìn)行業(yè)務(wù)處理
});
- js 中的new關(guān)鍵字
在java中通過new 來實例對象竟贯,但在js中已經(jīng)全部都是對象了盗誊,包括方法原型也是對象圣猎,new的本質(zhì)其實是繼承形成原型鏈士葫。
原型對象(prototype)函數(shù)獨有的屬性,構(gòu)造器原型(__ proto__)每個對象都有參考
在一個函數(shù)(函數(shù)鏈)中this 指向當(dāng)前函數(shù)所有者的對象送悔,在運行時確定具體的指向慢显,具體的調(diào)用指向爪模。
面向?qū)ο笈c原型原型參考
總結(jié)了4種單一的對象創(chuàng)建,和2種混合模式的創(chuàng)建荚藻。一般工廠模式和寄生模式屋灌,比較通用,像一些插件復(fù)雜的組件的編寫一般是構(gòu)造+原型的方式应狱。
- 一個基本的對象的創(chuàng)建
var user = new Object(); //創(chuàng)建一個Object對象
user.name = 'Lee';
user.age = 100;
user.run = function () { //創(chuàng)建一個run()方法并返回值
return this.name + this.age + '運行中...';
}
//字面量
var user={
name:'爸爸',
age:'18'
run:function(){ }
}
ps: 缺點共郭,代碼并不能復(fù)用,創(chuàng)建另一個對象就要產(chǎn)生大量的代碼
- 工廠模式
就是一個函數(shù)疾呻,然后放入?yún)?shù)除嘹,返回對象,解決了創(chuàng)建對象大量重復(fù)代碼的問題
function createObject(name, age) { //集中實例化的函數(shù)
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function () {
return this.name + this.age + '運行中...';
};
return obj;
}
ps: 調(diào)用的時候就像普通方法一樣調(diào)用岸蜗,缺點就是識別不了類型尉咕,工廠模式創(chuàng)建的對象都是object類型。有一定資源的消耗浪費
- 使用構(gòu)造函數(shù)
構(gòu)造函數(shù)方法名首字母約定大寫
function Box(name, age) { //構(gòu)造函數(shù)模式
this.name = name;
this.age = age;
this.run = function () {
return this.name + this.age + '運行中...';
};
}
var box = new Box();
ps:調(diào)用的時候通過new關(guān)鍵字進(jìn)行實例璃岳,否則就是類似于工廠模式普通的方法年缎。構(gòu)造函數(shù)沒有顯示的 new Object()(后臺進(jìn)行),this就代表這個對象,且沒有return語句铃慷。通過new 來調(diào)用的函數(shù) 會自動執(zhí)行如下操作 創(chuàng)建一個全新的對象-這個對象會被執(zhí)行[[prototype]]連接原型-函數(shù)調(diào)用中的this會綁定到新對象-如果函數(shù)沒有返回其他對象单芜,那么new 構(gòu)造就會自動返回這個新對象
。所以構(gòu)造函數(shù)可以box instanceof Box 確定實例類型枚冗,解決了工廠模式的弊端缓溅。但本質(zhì)和工程模式?jīng)]有什么區(qū)別而且實例調(diào)用方法的地址也都一樣。
-
使用原型
我們創(chuàng)建的每一個函數(shù)都有一個prototype(原型)屬性對象赁温,它由原型通過調(diào)用構(gòu)造函數(shù)創(chuàng)建坛怪。它可以使特定類型的所有實例共享屬性和方法,這個對象有實例指向原型對象的指針___ proto___ 屬性和指針指向構(gòu)造函數(shù)的constructor兩個屬性股囊。
//使用構(gòu)造的原型
function Box() {} //聲明一個構(gòu)造函數(shù)
Box.prototype.name = 'Lee'; //在原型里添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型里添加方法
return this.name + this.age + '運行中...';
};
//使用字面量的原型
function Box(){}; //聲明構(gòu)造函數(shù)
Box.prototype = {
name : 'Lee',
age : 100,
run : function () {
return this.name + this.age + '運行中...';
}
};
//字面量創(chuàng)建的方式使用constructor屬性不會指向?qū)嵗嗄洌鴷赶騉bject,構(gòu)造函數(shù)創(chuàng)建的方式則相反稚疹。
Box.prototype = {
constructor : Box, //直接強(qiáng)制指向?qū)嵗?};
ps:因為原型方法屬性都共享居灯,有時候往往也很致命(如存在引用類型)會有導(dǎo)致數(shù)據(jù)的污染。還有方法命名沖突内狗,且不利于維護(hù)的弊端怪嫌。關(guān)于js的原型鏈其實就是有限的實例對象和原型之間組成有限鏈來實現(xiàn)共享屬性和繼承的。當(dāng)查找屬性時先查找構(gòu)造函數(shù)實例里的屬性或方法柳沙,如果有岩灭,立刻返回;如果構(gòu)造函數(shù)實例里沒有赂鲤,則去它的原型對象里找噪径,如果有柱恤,就返回否則就返回undefiend。
- 創(chuàng)建對象比較好的方法找爱,使用混合模式
- 工廠模式+構(gòu)造函數(shù)模式(通用)梗顺。也叫寄生構(gòu)造函數(shù)
function Box(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function () {
return this.name + this.age + '運行中...';
};
return obj;
}
var box = new Box('Lee'); //實例
alert(box.rung());
ps: 和工廠模式區(qū)別就是 通過new調(diào)用。使用場景:比如創(chuàng)建具有額外方法的已有類型(如數(shù)組车摄,Date類型等)寺谤,但是又不污染原有的類型。 所以就算沒有new也可以得到對象(工廠模式)练般,只不過加上new讓人清楚這是新對象類型的實例矗漾,也是“寄生器構(gòu)造函數(shù)”里有個“構(gòu)造函數(shù)”的原因參考為什么要使用寄生
- 使用原型+構(gòu)造。優(yōu)秀的jquery插件大部分使用這種方式
function Box(name, age) { //不共享的使用構(gòu)造函數(shù)
this.name = name;
this.age = age;
this. family = ['父親', '母親', '妹妹'];
};
Box.prototype = { //共享的使用原型模式
constructor : Box,
run : function () {
return this.name + this.age + this.family;
}
};
升級版 也叫動態(tài)原型模式
function Box(name ,age) { //將所有信息封裝到函數(shù)體內(nèi)
this.name = name;
this.age = age;
if (typeof this.run != 'function') { //僅在第一次調(diào)用的初始化
Box.prototype.run = function () {
return this.name + this.age + '運行中...';
};
}
}
var box = new Box('Lee', 100);
alert(box.run());
污染數(shù)據(jù)的問題如下
function Box() {};
Box.prototype = {
constructor : Box,
name : 'Lee',
age : 100,
family : ['父親', '母親', '妹妹'], //添加了一個數(shù)組屬性
run : function () {
return this.name + this.age + this.family;
}
};
var box1 = new Box();
box1.family.push('哥哥'); //在實例中添加'哥哥'
alert(box1.run());
var box2 = new Box();
alert(box2.run()); //共享帶來的麻煩薄料,也有'哥哥'了
ps:這種‘原型+構(gòu)造’方式是創(chuàng)建對象比較好的方法敞贡,解決了數(shù)據(jù)污染的問題。有很多優(yōu)點摄职,就是寫的有點多誊役。
繼承
關(guān)于js繼承是都是依靠原型鏈實現(xiàn)的。
- 基本的原型鏈繼承把父類的實例作為子類的原型
具體查看
function Box() { //Box構(gòu)造
this.name = 'Lee';
}
function Desk() { //Desk構(gòu)造
this.age = 100;
}
Desk.prototype = new Box(); //Desc繼承了Box谷市,通過原型蛔垢,形成鏈條
ps:無法向父類傳參,來自原型對象的引用屬性都是共享的
- 構(gòu)造繼承
使用父類的構(gòu)造函數(shù)來增強(qiáng)子類實例迫悠,等于是復(fù)制父類的實例屬性給子類
function Box(age) {
this.name = ['Lee', 'Jack', 'Hello']
this.age = age;
}
function Desk(age) {
Box.call(this, age); //對象冒充鹏漆,給父類型傳參
}
vat desk = new Desk(100);
desk.name.push("hahah"); //只給desk添加新的數(shù)據(jù)
ps: 可以傳遞參數(shù),也可以不共享创泄,但不能使用父類的原型屬性和方法復(fù)用也就有影響艺玲,每個子類都有父類實例的復(fù)制影響性能
- 組合繼承
解決以上問題我們借用,原型鏈+借用構(gòu)造函數(shù)的模式鞠抑,通過調(diào)用父類構(gòu)造饭聚,繼承父類的屬性并保留傳參的優(yōu)點,然后通過將父類實例作為子類原型搁拙,實現(xiàn)函數(shù)復(fù)用
//定義父類
function Box(age) {
this.name = ['Lee', 'Jack', 'Hello']
this.age = age;
}
//父類的原型方法
Box.prototype.run = function () {
return this.name + this.age;
};
//子類
function Desk(age) {
Box.call(this, age); //對象冒充
}
Desk.prototype = new Box(); //原型鏈繼承
var desk = new Desk(100);
alert(desk.run());
ps:這是原型繼承和構(gòu)造繼承的所有優(yōu)點秒梳,唯一的缺點調(diào)用了兩次父類構(gòu)造函數(shù),生成了兩份實例箕速,子類實例覆蓋了子類原型的實例
- 寄生式組合繼承參考,組合模式和寄生組合模式區(qū)別
把原型式+工廠模式結(jié)合而來酪碘,封裝創(chuàng)建對象的過程。
//采用寄生組合繼承模式可以解決上述問題
function object(o) {
function F(){};
F.prototype = o;
return new F();
/*通過構(gòu)造一個介于superType與subType之間的對象盐茎,并使該對象的prototype屬性指向
superType prototype對象婆跑,來避開通過調(diào)用superType構(gòu)造函數(shù)的方式來產(chǎn)生一個prototype
指向superType prototype對象的對象。
有點繞庭呜,好好理解*/
};
function inheritPrototype(Sub, Super) {
/*這里為何需要調(diào)用object函數(shù)去構(gòu)造一個新的對象滑进,而不直接讓Sub.prototype=Super.prototype呢?
原因是如果這么做的話募谎,當(dāng)我們想給Sub的prototype里面添加共享屬性或者方法時扶关,如果其prototype指向的是Super的prototype,那么在Sub的prototype里添加的屬性和方法也會反映在Super的prototype里面数冬,這明顯是不合理的节槐,這樣做的后果是當(dāng)我們只想使用Super時,也能看見Sub往里面扔的方法和屬性拐纱。所以需要每個構(gòu)造函數(shù)都需要持有自己專用的prototype對象铜异。*/
var prototype = object(Super.prototype);
prototype.constructor = Sub;
Sub.prototype = prototype;
};
function Super(name) {
this.name = name;
if(typeof Super.prototype.sayName != "function") {
Super.prototype.sayName = function () { console.log(this.name)};
};
}
function Sub(name, age) {
Super.call(this, name);
this.age = age;
if(typeof Super.prototype.sayAge != "function") {
Super.prototype.sayAge = function () { console.log(this.age)};
};
};
inheritPrototype(Sub, Super);
var sub = new Sub("bobo", 26);
sub.sayAge();
sub.sayName();
console.log("typeof sub:" + typeof sub);
delete sub.name;
sub.sayName();//undifined
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(name){
Animal.call(this);
this.name = name || 'Tom';
Cat.prototype.constructor = Cat
}
(function(){
// 創(chuàng)建一個沒有實例方法的類
var Super = function(){};
Super.prototype = Animal.prototype;
//將實例作為子類的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
ps:解決了組合調(diào)用兩次的問題秸架,但是實現(xiàn)稍稍復(fù)雜
匿名函數(shù)的封裝和閉包
匿名函數(shù)就是沒有名字的函數(shù)揍庄,閉包是可訪問一個函數(shù)作用域里變量的函數(shù)。匿名函數(shù)的作用是避免全局變量的污染以及函數(shù)名的沖突
- 匿名函數(shù)
//通過表達(dá)式自我執(zhí)行
(function box() { //封裝成表達(dá)式
alert('Lee');
})(); //()表示執(zhí)行函數(shù)东抹,并且傳參
- 閉包
閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)蚂子,創(chuàng)建閉包的常見的方式,就是在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù)缭黔,通過另一個函數(shù)訪問這個函數(shù)的局部變量食茎。使用閉包作用:訪問局部變量,使變量常駐內(nèi)存馏谨。優(yōu)點可以避免使用全局變量别渔,避免數(shù)據(jù)的污染。由于作用域鏈的關(guān)系局部變量資源不會被立刻銷毀回收惧互,所以可能會占用更多的內(nèi)存哎媚。過度使用閉包會導(dǎo)致性能下降。
使用閉包訪問內(nèi)部變量
function f1(){
n=999;
function f2(){
alert(n);
}
//因為js鏈?zhǔn)阶饔糜虻慕Y(jié)構(gòu)壹哺,子對象會一級一級向上尋找父對象的變量抄伍,
對子對象都是可見的,反之不成立管宵。把可以訪問的f2作為f1的返回值截珍,
就能實現(xiàn)訪問內(nèi)部的變量。
return f2;
}
var result=f1();
result(); // 999
通過閉包使變量常駐內(nèi)存
function box() {
var age = 100;
return function () {
age ++;
return age;
}
}
var b = box(); //獲得函數(shù)
alert(b()); //調(diào)用匿名函數(shù)
alert(b()); //第二次調(diào)用匿名函數(shù)箩朴,實現(xiàn)累加
閉包中this 指向window
var user = 'The Window';
var obj = {
user : 'The Object',
getUserFunction : function () {
return function () { //閉包不屬于obj岗喉,里面的this指向window
return this.user;
};
}
};
alert(obj.getUserFunction()()); //The window
//可以強(qiáng)制指向某個對象
alert(obj.getUserFunction().call(obj)); //The Object
//也可以從上一個作用域中得到對象
getUserFunction : function () {
var that = this; //從對象的方法里得對象
return function () {
return that.user;
};
}
ie 中內(nèi)存泄露的問題
function box() {
var oDiv = document.getElementById('oDiv'); //oDiv用完之后一直駐留在內(nèi)存
oDiv.onclick = function () {
alert(oDiv.innerHTML); //這里用oDiv導(dǎo)致內(nèi)存泄漏
};
}
box();
那么在最后應(yīng)該將oDiv解除引用來避免內(nèi)存泄漏。
function box() {
var oDiv = document.getElementById('oDiv');
var text = oDiv.innerHTML;
oDiv.onclick = function () {
alert(text);
};
oDiv = null; //解除引用
}
ps:如果并沒有使用解除引用炸庞,那么需要等到瀏覽器關(guān)閉才得以釋放
實現(xiàn)塊級作用域钱床,進(jìn)行私有化
function box(count) {
for (var i=0; i<count; i++) {}
alert(i); //i不會因為離開了for塊就失效
}
//私有化變量
function box(count) {
(function () {
for (var i = 0; i<count; i++) {}
})();
alert(i); //報錯,無法訪問
}
//私有化變量和函數(shù)
var box = function () {
var age = 100;
function run() {
return '運行中...';
}
return { //直接返回對象
go : function () {
return age + run();
}
};
}();
上面的直接返回對象的例子埠居,也可以這么寫:
var box = function () {
var age = 100;
function run() {
return '運行中...';
}
var obj = { //創(chuàng)建字面量對象
go : function () {
return age + run();
}
};
return obj; //返回這個對象
}();
ps: 在全局作用域中使用塊級作用域可以減少閉包占用的內(nèi)存問題查牌,如果沒有指向匿名函數(shù)的引用事期。只要函數(shù)執(zhí)行完畢,就可以立即銷毀其作用域鏈了纸颜。
瀏覽器的檢測js的優(yōu)化
- 瀏覽器的識別
- navigator對象最早由Netscape Navigator2.0引入的navigator對象兽泣,現(xiàn)在已經(jīng)成為識別客戶端瀏覽器的事實標(biāo)準(zhǔn)
檢測插件
//列出所有的插件名
for (var i = 0; i < navigator.plugins.length; i ++) {
document.write(navigator.plugins[i].name + '<br />');
}
//檢測非IE瀏覽器插件是否存在
function hasPlugin(name) {
var name = name.toLowerCase();
for (var i = 0; i < navigator.plugins.length; i ++) {
if (navigator.plugins[i].name.toLowerCase().indexOf(name) > -1) {
return true;
}
}
return false;
}
alert(hasPlugin('Flash')); //檢測Flash是否存在
alert(hasPlugin('java')) //檢測Java是否存在
先寫到這里