<script>
</script>
<script src="./xx.js">
</script>
數值類: Number:包含浮點數,負數 javascript
NAN:不是數 使用isNan函數來判斷是否爲NAN css
Infinity無限大 html
字符串:以單引號或者雙引號括起來的任意文本 java
布爾值: || && node
比較運算符: == 比較時自動進行類型轉換,比較詭異,最好不要用 === 比較時不會進行類型轉換 web
常識:ajax
浮點數的比較由於精度問題不會相等 正則表達式
(1/3) === (1-2/3) //false chrome
緣由:浮點數的精度表示,應用Math.abs(1/3-(1-2/3))<0.0000001 json
null: 空 注意不是數字0 也不是空字符串「」
undefine: 未定義,在判斷是否傳入了參數的時候用 數組:
[]或者new Array(1,2,3);可經過[索引號]來訪問 用Array.isArray來判斷是否爲數組
對象:一組鍵值對構成想無序集合,能夠.或者[索引名]來訪問屬性
變量名的規則:大小寫英文字母,$ 與_構成的集合,不能以數字開頭,不能與關鍵字衝突。
賦值:= 聲明一個變量用var,不用var表示全局,在不一樣的js文件中衝突後會形成覆蓋影響。
${變量名}訪問變量
屬性:length 直接給length賦值會致使Array的大小發生變化。
索引賦值:var arr=[1,2,3]; arr[5]=‘x’; 此時arr變成[1,2,3,undefine,undefine,‘x’]
常見方法:
對象能夠視爲Map或者dictionary的一種表示,即鍵值對。 對象中的屬性key必須爲字符串,實際上爲Number或其餘類型也能夠,故引入map
方法:
set爲不可重複的key的集合,用add添加,delete刪除
iterable 適用於Array map set
1.可用for of來遍歷//此方法只遍歷屬於集合或字典自己的元素
例子:for (var x in arr)的缺點,for of 對此的改進
例如:var a=[1,2,3]; var a.name=‘hello’; for (var x in a){alert(x)};//會打印 ‘0’ ‘1’ ‘2’ ‘hello’forEach方法—iterable內置的方法,傳入一個函數,每次迭代都執行該回調函數
例如:iterable對象.forEach(function(element,index,this){})//對於set element和index爲同一個元素,由於它沒有索引。一個函數參數爲函數,此函數爲高階函數。var a =['A','B','C']; for (var i in a ){alert(i); alert(a[i]);
//過濾到繼承的屬性用hasOwnProperty
對於數字、字符串等是將它們的值傳遞給了函數參數,函數參數的改變不會影響函數外部的變量。
對於數組和對象等則是將對象(數組)的變量的值傳遞給了函數參數,這個變量保存的指向對象(數組)的地址。當函數改變這個地址指向的對象(數組)的內容時,同時也改變了函數外部變量指向的對象(數組)的內容;當函數改變的是變量的地址時,實際就與函數外部的變量失去了聯繫,變成了徹底不一樣的對象了,不會對函數外部對象形成改變。
var v1 = [] var v2 = {}; var v3 = {}; function foo(v1, v2, v3)//這裏分別是給v1 v2 v3 賦值新對象,對外部的對象不會有改變 { v1 = [1]; v2 = [2]; v3 = {a:3} } foo(v1, v2, v3); alert (v1); // 空白 alert (v2); // [object Object] alert (v3.a); // undefined
var v1 = [] var v2 = {}; var v3 = {a:0}; function foo(v1, v2, v3)//這裏不是賦值新對象,而是直接操做它,會形成函數外部對象改變 { v1.push (1); v2.a = 2; v3.a = 3; } foo(v1, v2, v3); alert (v1); // 1 alert (v2.a); // 2 alert (v3.a); // 3
定義:
1. function fun_name(prama1,prama2){…}
2. var var_name = function (prama1,prama2){};//注意最後有一個分號
方法2存在變量提高問題:
例如:
1. alarm(1); function alarm(){}
2. alarm(1); var alarm= function (){}//此處由於變量提高,實際順序爲 var alarm; alarm(1); alarm = function(){};//會在alarm(1)那句報錯,alarm is undefine
函數結束的兩種狀況:
1. return
2. 執行到末尾,無return,返回undefine
參數傳遞:
arguments關鍵字 像Array,實際不是Array。其屬性length存着參數個數。
..rest關鍵字 若是參數過多,用for循環一個個取的話比較麻煩
例如:
function (a,b ){ var i,var rest=[]; if(arguments.length>2) { for(i=2;i<arguments.length;i++) rest.push(arguments[i]) } }
function(a,b,...rest){}
返回值坑:
return 多行
return {
{name:'foo'}
{
var x = 1;
function bar()
{
var y = x+1;
}
var z = y+1;//reference Error
} * 變量提高,是由變量的查找機制引發的。 ‘use strict’
function foo()
{
var x = ‘hello ’+y;
alert(x);//打印爲 hello undefine
var y = ‘bob’;
}
因爲變量的查找機制,如今函數內查找,找到了,因而至關於這樣的順序 var y ; var x =「hello」+y; y=‘bob’。
如何簡單理解? 將內部全部var 定義都放在函數體前面。
名字空間:全局變量會綁定到window上,相同的全局變量會衝突
減小衝突的方法把本身用的全部變量和函數所有綁定到一個全局變量中
例如:
var MYAPP={};//MYAPP在這裏叫名字空間 JQUERY YUI underscore都是這麼幹的
MYAPP.name =「myapp」;
MYAPP.version = 1.0;
MYAPP.foo = function (){};
局部做用域:默認做用域爲函數內部
‘use strict’
function()
{
for(var i =0;i<100;i++){;}
i+=100;//仍然能夠引用該變量
}
常量
曾經:var PI = 3.14 ;//用大寫變量名錶示不要修改它
如今可用const,和let同樣具備塊級做用域。
例如:const P1=3.14; 修改不報錯,可是沒有效果
什麼是高階函數?
一個函數的參數爲一個函數。例如:
function add(x,y,f) { return f(x)+f(y); }
map
有一個函數f(x)= x2,做用在[1,2,3,4,5,6,7,8,9]上,map的實現以下:
實現:
function pow(x) { return x*x; } var arr=[1,2,3,4,5,6,7,8,9]; arr.map(pow);
等價形式:
for(var i =0 ;i<arr.length;i++){//此代碼缺點:不易讀懂,而map能夠一行搞定 result.push(pow(arr[i])); }
reduce
Array.reduce方法在[x1,x2,x3,x4]上,效果是:
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4);
用例:對一個Array求和的reduce實現
var arr=[1,3,5,7,9]; arr.reduce(function(x,y){ return x+y; });
filter
filter與map相似,但其是根據return 的false true來決定每一個元素的去留。map reduce filter 對undefine的處理?對map來講undefine仍是undefine 對filter不理不理undefine
sort:對Array直接修改,返回的是同一個Array
默認是按照ASCII碼排序
用例:
['Google','Apple','Microsoft'].sort();//['Apple','Google','Microsoft'] ['Google','Apple','Microsoft'].sort();//['Google','Microsoft','apple'] [10,20,1,2].sort();//[1,10,2,20]爲何?先轉化爲字符串再string排序
如何實現對數字排序?
傳入排序方法函數
var arr=[10,20,1,2]; arr.sort( function(x,y) { if(x>y) return 1;//將x後移 實現升序排列if(x<y) return -1; return 0; } );
什麼是閉包,與高階函數類似的概念
高階函數指接受一個函數做爲參數的函數。一樣函數也能夠做爲返回值,就叫閉包。
function sum(arr) { return arr.reduce( function(x,y) { return x+y; } ); } var f=sum([1,2,3,4,5]);//f接受一個函數 f();//調用,纔是真正計算結果。
內部函數sum能夠引用外部函數的參數和局部變量,相關參數和變量都般存在返回的函數中,稱爲「閉包」。
每次調用都產生一個新函數,即便參數相同。
閉包的用途
私藏變量:
用例:
function count() { var arr =[]; for(var i=1;i<=3;i++) { arr.push(function(){return i*i;}); } return arr; } var result = count(); result.map(funciton(x){console.log(x());})
//會打印16 16 16 爲何不是1 4 9
因爲變量的查找機制,arr中function沒有找不到i的定義,因而向外層函數找,找到時此變量值爲4。
返回函數不要引用任何循環變量,或者後續會發生變化的量
必定要引用循環變量怎麼辦,用閉包私藏起來,方法:建立一個匿名函數並當即執行,匿名函數引用變量並返回內部函數
function count() { var arr =[]; for(var i=1;i<=3;i++) { arr.push( (function (n){ return function(){return n*n;}; })(i);//建立匿名函數並當即執行,匿名函數參數爲i ); } return arr; }
建立匿名函數名當即執行:
function (x) {return x*x;}(x)
//理論上的寫法;但與函數體的定義衝突,Syntax Error (function (x) {return x*x;})(x)
//給函數的定義加上括號
私藏的變量,即時是外部也沒法訪問,並不與函數對象的原變量並非同一個。例子:
'use strict'function create_counter(initial) { var x = initial||0; return {//返回一個對象 inc:function(){ x+=1; return x; } } } //使用var c1= create_counter(); c1.inc();//1 c1.inc();//2
對於每一個返回的函數,其外部函數參數和局部變量,都是獨立的,而且相對於create_count.x也是獨立的
封裝函數
用例:常用x2和x3,可生成兩個函數,能夠簡化後期的參數輸入
function make_pow(n) { return function(x){ return Math.pow(x,n); }; } var pow2=make_pow(2); var pow3=make_pow(3); pow2(5);//25 pow3(7);//343
至關於匿名函數,當僅有一個表達式的時候,連{}和return 都省略了;有多條語句,就不能省略{}和return了。
f= x=> x*x; x=>{ if(x>0) return x*x; elsereturn -x*x*x; } (x,y)=>x*x+y*y;//兩個參數 ()=>3.14;//無參數 (x,y,...rest)=>{};//可變參數 x=>{foo:x};//返回一個對象//由於和函數體的定義衝突,須要改成 x=>({foo:x})
箭頭函數能夠解決函數內部定義函數this指向問題
var obj={ birth:1990, getAge: function () { var b = this.birth; // 1990
var fn = function () { return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn(); } }改進方法1 bind
getAge: function () { var b = this.birth; // 1990
var fn = function () { return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn().bind(this); }改進方法2 => :此時that就不須要了
var fn=()=>new Date().getFullYear()-this.birth;
//this此時指向window或undefine(strict模式下);
什麼是生成器? 標誌是 函數名前面有* 裏面有yield
可在執行過程當中返回屢次,像一個能夠記住狀態的函數。生成器函數返回的是一個生成器對象。
function * fab(max){ var t,a=0,b=1,n=1; while(n<max) { yield a; t=a+b; a=b; b=t; n++; } return a; }
var f = fab(5); f.next();//{value:0,done:false} f.next();//{value:0,done:false} f.next();//{value:0,done:true}//next方法須要咱們根據返回對象的done屬性判斷是否完成。
for (var x of fab(3))//用該方法不用判斷返回對象的done屬性
生成器的用途 解決回調地獄
ajax('http://url-1', data1, function (err, result) { if (err) { return handle(err); } ajax('http://url-2', data2, function (err, result) { if (err) { return handle(err); } ajax('http://url-3', data3, function (err, result) { if (err) { return handle(err); } return success(result); }); }); });上面的寫法回調越多,越難看
try { r1 = yield ajax('http://url-1', data1); r2 = yield ajax('http://url-2', data2); r3 = yield ajax('http://url-3', data3); success(r3); } catch (err) { handle(err); } //看上去同步的代碼,其實是異步的。只是寫法上的改變而已。yield與generator(*)結構,結合Promise的then catch,改善了異步回調的寫法,Async await生成方法被稱爲改善回調地獄的最後亮光。
var xiaoming = { name:"小明",'middle-school':'No.1 middle school'};//最後一個屬性不要加,
訪問屬性用.或者[‘屬性名’] 注意middle-school屬性並不是變量名,只能用xiaoming['middle-school]的方式來訪問。
檢測是否有某屬性/方法用in 。 ‘name’ in xiaoming ; ‘toString’ in xiaoming; toString定義在Object中,xiaoming的原型鏈指向Object。
檢測時自身擁有的屬性,仍是繼承來的用hasOwnProperty方法
什麼叫方法?將一個函數綁定到一個對象
例1:
var xiaoming ={ name:"小明", birth:1990; age:function(){ var y = new Date().getFullYear(); return y -this.birth; } }
執行xiaoming.age()//裏面的this指向xiaoming
例2://分開寫
function getAge(){ var y = new Date().getFullYear(); return y -this.birth; } var xiaoming ={ name:"小明", birth:1990; }
執行xiaoming.getAge()//裏面的this指向xiaoming 執行getAge() //返回NAN this指向window
例3:
'use strict'var xiaoming ={ name:"小明", birth:1990; age:function(){ var y = new Date().getFullYear(); return y -this.birth; } } var fn = xiaoming.age(); fn();//非strict下指向window,strict下指向undefine
例4:
var xiaoming ={ name:"小明", birth:1990; age:function(){//函數內部定義函數this的問題function getAgeFromBirth() { var y = new Date().getFullYear(); return y-this.birth; } return getAgeFromBirth(); } }
執行xiaoming.age()//裏面的this非strict下指向window,strict下指向undefine。
修正:
var xiaoming ={ name:"小明", birth:1990; age:function(){//函數內部定義函數this的問題 that = this; function getAgeFromBirth() { var y = new Date().getFullYear(); return y-that.birth; } return getAgeFromBirth(); } }
對於函數內定義的函數,this非strict下指向window,strict下指向undefine。
如何控制this的指向? call apply bind
使用方式:
裝飾器模式的實現 使用apply實現
例如:統計一下使用了多少次parseInt
var count = 0; var oldparseInt = pareseInt;//保存原函數 window.parseInt = function () { count+=1; return oldparseInt.apply(null,arguments); } parseInt('10'); parseInt('20'); parseInt('30');
typeof 123;//'number'
typeof NaN;//'number'
typeof 'str';//'string'
typeof true;//'boolean'
typeof undefine;//'undefined'
typeof Math.abs;//'function'
typeof null;//'object'
typeof [];//'object'
typeof {};//'object'
typeof new String('str');//'object'new String('str') ==='str'//false不要使用包裝對象
總結:
不要使用new Number()、new Boolean()、new String()建立包裝對象;
用parseInt()或parseFloat()來轉換任意類型到number;
用String()來轉換任意類型到string,或者直接調用某個對象的toString()方法;
一般沒必要把任意類型轉換爲boolean再判斷,由於能夠直接寫if (myVar) {…};
typeof操做符能夠判斷出number、boolean、string、function和undefined;
判斷Array要使用Array.isArray(arr);
判斷null請使用myVar === null;
判斷某個全局變量是否存在用typeof window.myVar === ‘undefined’;
函數內部判斷某個變量是否存在用typeof myVar === ‘undefined’。
最後有細心的同窗指出,任何對象都有toString()方法嗎?null和undefined就沒有!雖然null還假裝成了object類型。
更細心的同窗指出,number對象調用toString()報SyntaxError:
123.toString(); // SyntaxError
遇到這種狀況,要特殊處理一下: 123..toString(); // ‘123’, 注意是兩個點! 由於Number類型有浮點數 (123).toString(); // ‘123’
var now = new Date(); now;//var now = Date(); now;//wed jun 24 2015 19:49:22 GMT +800(CST) now.getFullYear();//2015 now.getMonth();//0-11 now.getDate();//24 24號 now.getDay();//星期 這個比較特別 其餘的都是年月日時分秒毫秒 now.getHours();//24小時制 now.getMinutes();//49分鐘 now.getSeconds();//秒 now.getMilliseconds();//毫秒數 now.getTime();//時間戳 13位 以Number形式,表示1970年1月1日0點整通過的毫秒數,在GMT時區下
new Date(2015,5,19,20,3,3,123);//年月日時分秒毫秒時區
var d = Date.parse('2015-06-24T19:24:22.876+0800');//T後面接時間 +/-hh:mm表示相對於UTC超前/滯後的時間
var time = Date(d);//將時間戳轉化爲人能夠讀懂的時間 now.toLocaleString();//轉換爲當地時間 now.toTimeString();//UTC+8爲中國時間
時區知識:GMT 格林威治時間 UTC世界協調時間 DST夏日節約時間 CST同時表示4個時區,用+hh:mm表示區別
匹配字符串強有力的武器,凡是符號規則的字符串。
入門:
直接給出某字符爲精確匹配
\d 匹配一個數字
\w一個字母或數字或與[a-zA-Z0-9]等效
. 任意一個字符
變長任意個字符
至少一個字符
? 0個或1個字符
{n} n個字符
{n,m} n-m個字符
例子: \d{3}\S+\d{3,8}
\d{3}表示匹配3個數字
\S+ 表示至少一個空白字符(空格 tab等)
\d{3,8}表示3-8個數字
\d{3}\-\d{3,8} 能夠匹配010-1235
-是特殊字符須要轉義
進階:
[]表示字符範圍,如[0-9a-z]
[]可用A|B,表示匹配A或者B
表示行頭,在[]中表示非 ^\d必須以數字開頭
$表示行尾
\d$必須以數字結尾
正則表達式的建立
/正則表達式/ 去掉裏面的正則表達式,則變成了行註釋的符號
new RegExp(‘正則表達式’); ‘ABC\-001’ \實際爲一個,須要轉義 用途: 1.測試給定的字符串是否符合條件 2.用於切分字符串 split
'ab c'.split('');//['a','b',' ',' ','c']存在多個空格符怎麼辦
'ab c'.split(/\s+/);//['a','b',' ',' ','c'] 存在","怎麼辦?
'a,b, c'.split(/[\s,]+/);//['a','b',' ',' ','c'] 存在";"呢?
'a,b; c'.split(/[\s,;]+/);//['a','b',' ',' ','c']
相關函數: match一個或多個,返回數組 search 第一個 replace split
分組和提取子串 子表達式的反向引用\2 \1
var re = /^(\d{3}-(\d{3,8}))/; re.exec('010-12345');//['010-12345','010','12345']; re.exec('010 12345');//null
/(\d)(\d)\2\1/;//能夠匹配5775 1221 之類的字符串 \2 反向引用子式2 \1反向引用子式1
全局搜索 g(可執行屢次匹配,不加指匹配一次) i(忽略大小寫) m多行匹配(影響多行中^$的匹配)
var r1=/test/g;//或者 var r1=new RegExp(‘test’,‘g’);
可執行屢次exec,每次exec會更新lastIndex屬性 lastIndex leftcontent rightcontent
var s = 'JavaScript, VBScript, JScript and ECMAScript'; var re=/[a-zA-Z]+Script/g; // 使用全局匹配: re.exec(s); // ['JavaScript'] re.lastIndex; // 10 re.exec(s); // ['VBScript'] re.lastIndex; // 20 re.exec(s); // ['JScript'] re.lastIndex; // 29 re.exec(s); // ['ECMAScript'] re.lastIndex; // 44 re.exec(s); // null,直到結束仍沒有匹配到
正則表達式的侷限:像‘2-30’ ‘4-31’這樣的非法日期,用正則表達式是識別不了或者實現困難,要配合程序來處理
正則表達式的貪婪匹配:
var re=/^(\d+)(0*)/; re.exec('102300');//["102300","102300",""]//加個?表示非貪婪匹配 re=/^(\d+?)(0*)/; re.exec('102300');//["102300","1023","00"] re=/1{3}/;//匹配11111111 會獲得 111 111 第一次匹配會更新lastIndex屬性
元字符的分類:1.限定次數符 {n} {n,m} + * ? 2.選擇匹配符 3.分組組合與反向引用 4.特殊字符 5.字符匹配符 6.定位符
JSON以前一直使用XML,猶豫XML規範衆多,Douglas發明了JSON。
JSON中一共有number、boolean、string、null、array([])、object({…}),並規定死了字符集爲UTF-8,規定字符串必須用「」,
object的鍵也必須用「」。
方法:
序列化—字符串化
使用JSON類的stringify(對象,篩選屬性數組或一個函數,每一個鍵值對都會被函數先處理,縮進)。
寫出對象的TOJSON方法,返回JSON應該序列號的數據
反序列化—將其轉換爲對象
使用eval執行表達式,太過危險
JSON.parse(字符串,函數) 傳入函數會處理鍵值對
javascript不區分類和實例的概念,而是經過原型(prototype)來實現。
var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } }; var xiaoming = { name: '小明' }; xiaoming.__proto__ = Student; //把xiaoming的原型指向了對象Student,看上去xiaoming彷彿是從Student繼承下來的
javascript全部對象都是實例,繼承是把一個對象的原型指向另外一個對象。 實際中不要使用__proto__
屬性改變一個對象原型,
可經過編寫函數配合Object.create(傳入原型)的方法:
例如:
// 原型對象:var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } }; function createStudent(name) { // 基於Student原型建立一個新對象:
var s = Object.create(Student); // 初始化新對象: s.name = name; return s; } var xiaoming = createStudent('小明'); xiaoming.run(); // 小明 is running... xiaoming.__proto__ === Student; // true
每建立一個對象都會設置一個原型
屬性/方法訪問機制
當訪問obj.xxx屬性時,如今當前對象查找屬性,沒找到,再到原型對象找,還未找到,再到Object.prototype對象,
若是仍然沒有找到,放回undefine
原型鏈
var arr=[1,2,3]; //原型鏈爲 arr->Array.prototype->object.prototype->nullfunction foo(){return 0;} //原型鏈爲 foo->Function.prototype->object.prototype->null//Function中有Apply方法
使用構造函數建立對象
function Student(name) {//注意建立對象的函數首字母爲大寫,普通函數爲小寫this.name = name; this.hello = function () { alert('Hello, ' + this.name + '!'); } } var xiaoming = new Student(' 小明'); //寫new的做用 函數會變成構造函數: 1. 內部this指向新建立的對象 2 默認返回this
//不寫new 返回undefine
若是還有別的對象,其原型鏈爲:
xiaoming ↘
xiaohong -→ Student.prototype —-> Object.prototype —-> null
xiaojun ↗
xiaoming.constructor === Student.prototype.constructor; // true Student.prototype.constructor === Student; // true Object.getPrototypeOf(xiaoming) === Student.prototype; // true xiaoming instanceof Student; // true
//函數Student剛好有個屬性prototype指向xiaoming、xiaohong的原型對象
//可是xiaoming、xiaohong這些對象可沒有prototype這個屬性,
//不過能夠用__proto__這個非標準用法來查看。
如何共享同一個函數,而不是每一個對象各有一份代碼?
xiaoming.hello === xiaohong.hello;//false
將函數寫入prototype,修改代碼以下:
function Student(name) { this.name = name; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); };//注意此處有分號
構造函數忘記寫new會怎樣?
在strict模式下,this執行undefine,非stric模式下,this執行window,會建立全局變量;jslint能夠檢查出漏泄的new
如何防止漏寫new? 編寫函數,將new封到函數裏面
function Student(props) { this.name = props.name || '匿名'; // 默認值爲'匿名'
this.grade = props.grade || 1; // 默認值爲1 } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); }; function createStudent(props) { return new Student(props || {}) } var xiaoming = createStudent({//傳入參數 name: '小明' }); //好處 1.不用new來調用 2.傳入參數靈活,因爲參數是對象,咱們能夠把JSON轉爲對象直接傳入。 xiaoming.grade; // 1 修改參數
基於上街Student擴展出PrimaryStudent,能夠先定義出PrimaryStudent:
function PrimaryStudent(props) { // 調用Student構造函數,綁定this變量: Student.call(this, props); this.grade = props.grade || 1; }
調用了Student構造函數不等於繼承了Student,PrimaryStudent建立的對象的原型是:
new PrimaryStudent() —> PrimaryStudent.prototype —> Object.prototype —> null
必須想辦法把原型鏈修改成:
new PrimaryStudent() —> PrimaryStudent.prototype —> Student.prototype —>Object.prototype —> null
PrimaryStudent對象既能夠調用PrimaryStdent.prototype定義獲得方法,也能夠調用原型鏈上的方法。
若是你想用最簡單粗暴的方法這麼幹:
PrimaryStudent.prototype = Student.prototype;
是不行的!若是這樣的話,PrimaryStudent和Student共享一個原型對象,那還要定義PrimaryStudent幹啥?
咱們必須藉助一箇中間對象來實現正確的原型鏈,這個中間對象的原型要指向Student.prototype。
爲了實現這一點,參考道爺(就是發明JSON的那個道格拉斯)的代碼,中間對象能夠用一個空函數F來實現:
// PrimaryStudent構造函數:function PrimaryStudent(props) { Student.call(this, props); this.grade = props.grade || 1; } // 空函數F:function F() { } // 把F的原型指向Student.prototype: 1.先指明F對象建立模板爲Student,F內才能找到Student對象的屬性 F.prototype = Student.prototype; // 把PrimaryStudent的原型指向一個新的F對象,F對象的原型正好指向Student.prototype: PrimaryStudent.prototype = new F();//2// 把PrimaryStudent原型的構造函數修復爲PrimaryStudent: PrimaryStudent.prototype.constructor = PrimaryStudent;//3
// 繼續在PrimaryStudent原型(就是new F()對象)上定義方法: PrimaryStudent.prototype.getGrade = function () { return this.grade; }; // 建立xiaoming:var xiaoming = new PrimaryStudent({ name: '小明', grade: 2 }); xiaoming.name; // '小明' xiaoming.grade; // 2// 驗證原型: xiaoming.__proto__ === PrimaryStudent.prototype; // true xiaoming.__proto__.__proto__ === Student.prototype; // true// 驗證繼承關係: xiaoming instanceof PrimaryStudent; // true xiaoming instanceof Student; // true
進一步封裝
若是把繼承這個動做用一個inherits()函數封裝起來,還能夠隱藏F的定義,並簡化代碼:
function inherits(Child, Parent) {//此函數能夠複用
var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; }
使用:
function Student(props) { this.name = props.name || 'Unnamed'; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); } function PrimaryStudent(props) { Student.call(this, props); this.grade = props.grade || 1; } // 實現原型繼承鏈: inherits(PrimaryStudent, Student); // 綁定其餘方法到PrimaryStudent原型: PrimaryStudent.prototype.getGrade = function () { return this.grade; };
JavaScript的原型繼承實現方式就是:
定義新的構造函數,並在內部用call()調用但願「繼承」的構造函數,並綁定this;
藉助中間函數F實現原型鏈繼承,最好經過封裝的inherits函數完成;
繼續在新的構造函數的原型上定義新方法。
在上面的章節中咱們看到了JavaScript的對象模型是基於原型實現的,特色是簡單,缺點是理解起來比傳統的類-實例模型要困難,
最大的缺點是繼承的實現須要編寫大量代碼,而且須要正確實現原型鏈。
有沒有更簡單的寫法?有!
新的關鍵字class從ES6開始正式被引入到JavaScript中。class的目的就是讓定義類更簡單。
咱們先回顧用函數實現Student的方法:
function Student(name) { this.name = name; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); }
若是用新的class關鍵字來編寫Student,能夠這樣寫:
class Student { constructor(name) { this.name = name; } hello() { alert('Hello, ' + this.name + '!'); } }
比較一下就能夠發現,class的定義包含了構造函數constructor和定義在原型對象上的函數hello()(注意沒有function關鍵字),
這樣就避免了Student.prototype.hello = function () {…}這樣分散的代碼。
最後,建立一個Student對象代碼和前面章節徹底同樣:
var xiaoming = new Student('小明'); xiaoming.hello();
class繼承
用class定義對象的另外一個巨大的好處是繼承更方便了。想想咱們從Student派生一個PrimaryStudent須要編寫的代碼量。
如今,原型繼承的中間對象,原型對象的構造函數等等都不須要考慮了,直接經過extends來實現:
class PrimaryStudent extends Student { constructor(name, grade) { super(name); // 記得用super調用父類的構造方法!this.grade = grade; } myGrade() { alert('I am at grade ' + this.grade); } }
注意PrimaryStudent的定義也是class關鍵字實現的,而extends則表示原型鏈對象來自Student。子類的構造函數可能會與父類不太相同,
例如,PrimaryStudent須要name和grade兩個參數,而且須要經過super(name)來調用父類的構造函數,不然父類的name屬性沒法正常初始化。
PrimaryStudent已經自動得到了父類Student的hello方法,咱們又在子類中定義了新的myGrade方法。
ES6引入的class和原有的JavaScript原型繼承有什麼區別呢?實際上它們沒有任何區別,class的做用就是讓JavaScript引擎去實現原來須要咱們
本身編寫的原型鏈代碼。簡而言之,用class的好處就是極大地簡化了原型鏈代碼。
你必定會問,class這麼好用,能不能如今就用上?
如今用還早了點,由於不是全部的主流瀏覽器都支持ES6的class。若是必定要如今就用上,就須要一個工具把class代碼轉換爲傳統的prototype代碼,
能夠試試Babel這個工具。
現有的瀏覽器內核與javascript引擎
window 對象不但充當全局做用域,並且表示瀏覽器窗口。
navigator對象表示瀏覽器的信息,最經常使用的屬性包括:
var width= window.innerWidth||document.body.clientWidth;
screen對象表示屏幕的信息,經常使用的屬性有:
location對象表示當前頁面的URL信息。例如,一個完整的URL:http://www.example.com:8080/path/index.html?a=1&b=2#TOP
屬性:
方法:
document對象表示當前頁面。因爲HTML在瀏覽器中以DOM形式表示爲樹形結構,document對象就是整個DOM樹的根節點。
屬性:
title 是從title標籤內容中讀取的,能夠動態修改。
document.title = ‘努力學習JavaScript!’;
cookie
Cookie是由服務器發送的key-value標示符。由於HTTP協議是無狀態的,可是服務器要區分究竟是哪一個用戶發過來的請求,就能夠用Cookie來區分。
當一個用戶成功登陸後,服務器發送一個Cookie給瀏覽器,例如user=ABC123XYZ(加密的字符串)…,此後,瀏覽器訪問該網站時,會在請求頭附上這個Cookie,服務器根據Cookie便可區分出用戶。
javascript可經過document.cookie來讀取到當前頁面的cookie。若是瀏覽器嵌入了第三方的javascript,會形成巨大隱患。
服務器在設置cookie時可使用httponly這樣的選項,這樣的cookie就不能經過javascript代碼讀取了。
獲取節點的方法
var menu = document.getElementById(''); menu.tagName;//輸出標籤名var drinks = document.getElementByTagName('');
history
方法:
實現無刷新更換URL地址
window.history.pushState({},0,url);
利用ajax配合pushState翻頁無刷新的動做
因爲HTML文檔被瀏覽器解析後就是一棵DOM樹,要改變HTML的結構,就須要經過JavaScript來操做DOM。
樹形結構:
更新:更新該DOM節點的內容,至關於更新了該DOM節點表示的HTML的內容;
遍歷:遍歷該DOM節點下的子節點,以便進行進一步操做;
添加:在該DOM節點下新增一個子節點,至關於動態增長了一個HTML節點;
刪除:將該節點從HTML中刪除,至關於刪掉了該DOM節點的內容以及它包含的全部子節點。
遍歷獲取DOM節點—在操做一個DOM節點前,咱們須要經過各類方式先拿到這個DOM節點。
經過document的方法和屬性
[索引號]
[索引號]
得到某節點下的全部直屬點
經過屬性:
經過選擇器方法querySelector 和QuerySelectorAll() 須要瞭解select的用法,相似於JQUERY 的選擇器。
node和element的區別?
node更爲廣義,包括了element、comment、CDATA_SECTION等多種,但絕大時候,咱們只關心element,由於他是能夠看到的,comment這種是不可見的。
插入 若是dom節點是空的,能夠經過innetHTML來寫
appendChild 插入到父節點的最後一個節點。例如:
var d = document.createElement('style'); d.setAttribute('type', 'text/css'); d.innerHTML = 'p { color: red }'; document.getElementsByTagName('head')[0].appendChild(d);
parentElement.insertBefore(newElement,referenceElement) 插入到參照節點前
haskell = document.createElement('p'); haskell.id = 'haskell'; haskell.innerText = 'Haskell'; list.insertBefore(haskell, ref);
刪除—要刪除一個節點,首先要得到該節點自己以及它的父節點,而後,調用父節點的removeChild把本身刪掉:
removeChild
刪除後的節點雖然不在文檔樹中了,但其實它還在內存中,能夠隨時再次被添加到別的位置。
children屬性是一個只讀屬性,而且它在子節點變化時會實時更新。更新—也就是修改
<span>
’被編碼爲‘<span>
’保證其不會產生任何HTML標籤。
修改CSS,經過節點的style屬性,因爲CSS容許font-size並不是javascript有效的屬性名,須要將其改成駝峯式,fontSize
p.style.fontSize='20px';
表單爲能夠得到用戶輸入的內容,或者對輸入框設置新的內容。
表單控件
文本框,對應的<input type="text">
,用於輸入文本;
口令框,對應的<input type="password">
,用於輸入口令;
單選框,對應的<input type="radio">
,用於選擇一項;同一組單選框name屬性必須一致
複選框,對應的<input type="checkbox">
,用於選擇多項;同一組複選框name必須一致
下拉框,對應的<select>
<option value=""></option>
,用於選擇一項;
隱藏文本,對應的<input type="hidden">
,用戶不可見,但表單提交時會把隱藏文本發送到服務器。
獲取表單值的方式
爲輸入框設置label標籤的函數,單擊該處文本,焦點會自動跑到對應輸入框上。增大了可單擊區域。
// <label><input type="radio" name="weekday" id="monday" value="1"> Monday</label>
// <label><input type="radio" name="weekday" id="tuesday" value="2"> Tuesday</label>
var mon = document.getElementById('monday'); var tue = document.getElementById('tuesday'); mon.value; // '1' tue.value; // '2' mon.checked; // true或者false tue.checked; // true或者false
設置表單值的方式
HTML5表單控件
<input type="date" value="2015-07-01">
日期
<input type="datetime-local" value="2015-07-01T02:03:04">
日期和本地時間
<input type="color" value="#ff0000">
顏色選擇器
對於不支持的瀏覽器會看成 type=「text」來顯示。
提交表單的兩種方式
<button type="submit">
時提交表單,或者用戶在最後一個輸入框按回車鍵。須要使用form的onSubmit屬性 <!-- HTML -->
<form id="test-form" onsubmit="return checkForm()">
<input type="text" name="test">
<button type="submit">Submit</button>
</form>
<script> function checkForm() { var form = document.getElementById('test-form'); // 能夠在此修改form的input...// 繼續下一步:return true; //return flase則不會提交 } </script>
防止提交表單時輸入框閃爍 在檢查和修改<input>
時,要充分利用<input type="hidden">
來傳遞數據。
例如,不少登陸表單但願用戶輸入用戶名和口令,可是,安全考慮,提交表單時不傳輸明文口令,而是口令的MD5。
普通JavaScript開發人員會直接修改<input>
:
這個作法看上去沒啥問題,但用戶輸入了口令提交時,口令框的顯示會忽然從幾個變成32個(由於MD5有32個字符)。
要想不改變用戶看到這個現象,能夠利用<input type="hidden">
實現:
<form id="login-form" method="post" onsubmit="return checkForm()">
<input type="text" id="username" name="username">
<input type="password" id="input-password">
<input type="hidden" id="md5-password" name="password">
<button type="submit">Submit</button>
</form>
<script> function checkForm() { var input_pwd = document.getElementById('input-password'); var md5_pwd = document.getElementById('md5-password'); // 把用戶輸入的明文變爲MD5: md5_pwd.value = toMD5(input_pwd.value); // 繼續下一步:return true; } </script>
在HTML表單中,能夠上傳文件的惟一控件就是<input type="file">
。
注意:當一個表單包含<input type="file">
時,表單的enctype必須指定爲multipart/form-data,method必須指定爲post,
瀏覽器才能正確編碼並以multipart/form-data格式發送表單的數據。
出於安全考慮,瀏覽器只容許用戶點擊<input type="file">
來選擇本地文件,用JavaScript對<input type="file">
的value賦值是沒有任何效果的。
當用戶選擇了上傳某個文件後,JavaScript也沒法得到該文件的真實路徑。
一般,上傳的文件都由後臺服務器處理,JavaScript能夠在提交表單時對文件擴展名作檢查,以便防止用戶上傳無效格式的文件:
var f = document.getElementById('test-file-upload'); var filename = f.value; // 'C:\fakepath\test.png'
if (!filename || !(filename.endsWith('.jpg') || filename.endsWith('.png') )) { alert('Can only upload image file.'); return false; }
因爲JavaScript對用戶上傳的文件操做很是有限,之前都是用flash來上傳,能夠獲得上傳進度條等信息。
HTML5新增的File API容許JavaScript讀取文件內容,得到更多的文件信息。 可經過name size lastModifiedDate獲取信息。
// 讀取文件:var reader = new FileReader(); reader.onload = function(e) {//回調函數,不知道何時讀完,讀完後自動調用咱們設置的函數var data = e.target.result; // 'data:image/jpeg;base64,/9j/4AAQSk...(base64編碼)...' preview.style.backgroundImage = 'url(' + data + ')'; }; // 以DataURL的形式讀取文件: reader.readAsDataURL(file);//以Base64方式,經常使用於設置圖像
提交表單時會刷新頁面,告訴你操做成功/失敗,因爲網絡太慢或者其餘緣由,可能獲得404。
web的運做原理:一次HTTP請求對應一個頁面 如何讓用戶停留在該頁面,用javascript發送請求並接受數據,更新頁面—Ajax
var request; if (window.XMLHttpRequest) {//經過該屬性肯定是否支持標準XMLHttpRequest
// 不要判斷navigator.userAgent 1.能夠僞造 2.判斷IE版本複雜 request = new XMLHttpRequest(); } else { request = new ActiveXObject('Microsoft.XMLHTTP'); } request.onreadystatechange = function () { // 狀態發生變化時,函數被回調
if (request.readyState === 4) { // 成功完成// 判斷響應結果:
if (request.status === 200) { // 成功,經過responseText拿到響應的文本:
return success(request.responseText);
} else { // 失敗,根據響應碼判斷失敗緣由:
return fail(request.status);
} } else { // HTTP請求還在繼續... } } // 發送請求: request.open('GET', '/api/categories'); //open方法有三個參數 第一個參數爲請求的方法 ,
//第二個爲請求的URL地址,第三個參數代表是否使用異步
//第三個參數默認爲true 使用異步,若是爲同步,瀏覽器中止響應知道Ajax完成。 request.send(); request.open('POST', '/api/categories'); request.setHeader//POST請求須要設置HEADER senStr= request.send();//POST請求須要在send中寫上要發送的參數;GET不須要,由於參數在URL中
跨域訪問—javascript的安全策略
javascript在發送請求時,URL域名必須和當前頁面一致,一致的意思是:
若是要訪問外域(其餘網站)的URL呢?
<script src="http://api.126.net/....?callback=refreshPrice"></script>
取決於sina.com是否願意將你加入Access-Control-Allow-Origin:或者*
上面這種跨域請求,稱之爲「簡單請求」。簡單請求包括GET、HEAD和POST(POST的Content-Type類型
僅限application/x-www-form-urlencoded、multipart/form-data和text/plain),而且不能出現任何自定義頭(例如,X-Custom: 12345) CSS中的跨域請求
/* CSS */ @font-face { font-family: 'FontAwesome'; src: url('http://cdn.com/fonts/fontawesome.ttf') format('truetype'); /*CDN服務器的Access-Control=Allow-Origin不給權限,則沒法加載字體資源*/ }
對於PUT、DELETE以及其餘類型如application/json的POST請求,在發送AJAX請求以前,瀏覽器會先發送一個OPTIONS請求
(稱爲preflighted請求)到這個URL上,詢問目標服務器是否接受:
OPTIONS /path/to/resourceHTTP/1.1 Host: bar.com Origin: http://my.com Access-Control-Request-Method: POST
服務器必須響應並明確指出容許的Method,纔會繼續發送Ajax,不然拋出錯誤:
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://my.com Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS Access-Control-Max-Age: 86400
因爲以POST、PUT方式傳送JSON格式的數據在REST中很常見,因此要跨域正確處理POST和PUT請求,服務器端必須正確響應OPTIONS請求。
在javascript中全部代碼都是單線程執行的。 全部javascript中全部的網絡操做,瀏覽器事件都必須異步執行,異步執行可用回調函數實現。
異步函數會在未來的某個時間點出發一個函數調用。 如Ajax
request.onreadystatechange = function () { // 狀態發生變化時,函數被回調
if (request.readyState === 4) { // 成功完成// 判斷響應結果:
if (request.status === 200) { // 成功,經過responseText拿到響應的文本:
return success(request.responseText); } else { // 失敗,根據響應碼判斷失敗緣由:
return fail(request.status);
} } else { // HTTP請求還在繼續... } }
假若有以下的寫法,先統一執行ajax邏輯,不關心結果如何,
var ajax=ajaxGet("http://...");
而後根據成功、失敗調用sucess或者fail函數。
ajax.ifSuccess(success); ajax.ifFail(fail);
Promise的then函數接受一個處理函數,此函數在處理成功時調用;catch接受一個處理函數,此函數在失敗時調用。 例如:
function test(resolve, reject) { //若是成功調用resolve resolve('200 OK'); //若是失敗,調用reject reject('timeout in ' + timeOut + ' seconds.'); } var p1 = new Promise(test);//test爲要執行的函數,接收兩個函數做爲參數,一個在成功時用,另外一個在失敗時
var p2 = p1.then(function (result) { console.log('成功:' + result); }); var p3 = p2.catch(function (reason) { console.log('失敗:' + reason); });
var job1 = new Promise(function (resolve, reject) { log('start new Promise...'); resolve(123); }); job1.then(job2).then(job3).catch(handleError);//鏈式處理
同步
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1'); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2'); }); // 同時執行p1和p2,並在它們都完成後執行then: Promise.all([p1, p2]).then(function (results) { console.log(results); // 得到一個Array: ['P1', 'P2'] });
有一個返回便可收工
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1'); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2'); }); //只須要得到先返回的結果便可,多餘是爲了容錯 Promise.race([p1, p2]).then(function (result) { console.log(result); // 'P1' });
Promise能夠把不少異步任務以並行和串行的方式組合起來執行。
沒有canvas,繪圖只能用flash插件,用javascript與flash進行交互。
<canvas id="test-stock" width="300px" height="200px">
<p>你的瀏覽器不支持canvas</p>
</canvas>
getContext(‘2d’)方法會讓咱們拿到CanvasRendringContext2D對象,全部繪圖都要經過這個對象來完成。
3D圖像要使用WebGL,getContext(‘Webgl’)
canvas座標系統
Stroke 勾勒邊框 反義詞 fill來進行填充
canvas除了能繪製基本形狀和文本,還能夠實現動畫、縮放、各類濾鏡、像素變換等高級操做,若是要實現很複雜的操做,從如下幾條優化: