上一篇文章已經聊過JavaScript的歷史了這裏再也不復述了直接切入正題,JavaScript是一門解釋型、動態類型、弱類型語言。javascript
解釋型語言和編譯型語言就相似看一本書,編譯型語言就是直接把整本書給你翻譯成中文讓你看,效率高。解釋型就是給你找一個翻譯翻譯一句你讀一句相對編譯型效率就底一些css
動態類型語言:就是一個變量自己的類型不是在定義的時候就指定類型了,而是在在運行的時候動態賦值這個變量是什麼類型的語言html
弱類型語言與之對比的就是強類型語言拿Python舉例:java
# 定義一個變量 test_value = 1 # 咱們賦值的時候已經指定這個變量爲int類型咱們可使用進行int類型的操做 print(test_value + 1) # 可是若是咱們使用它進行字符串操做就會報錯 print(test_value + "str_value") """ TypeError: unsupported operand type(s) for +: 'int' and 'str' """
強類型語言,如今若是你想用test_value進行字符串操做只能把它轉爲str類型python
在看一下弱類型的代碼JavaScript程序員
let testValue = 1; // 如今咱們定義一個testValue爲int類型並進行int console.log(testValue + 1); // 可是現下面的操做 console.log(testValue + "Hello World"); // 如今咱們作字符串的拼接而這時候不須要轉換,弱類型有時顯得很方便,有時卻又極易出錯
哦~這裏定義變量的時候ECMAScript6之後定義變量var將推出歷史舞臺改成:let 和const了正則表達式
看一下HTML和JS的加載順序編程
1.瀏覽器加載和渲染html的順序數組
2. JS的加載瀏覽器
3.如何加快HTML頁面加載速度
4.HTML頁面加載和解析流程
5.Yahoo對網頁設計性能的建議,我的感受是說得很是好的。
英文版:http://developer.yahoo.com/performance/rules.html
中文翻譯:http://www.cnblogs.com/smjack/archive/2009/02/24/1396895.html
原文連接:http://renyongjie668.blog.163.com/blog/static/1600531201097062789/
因此咱們建議通常JavaScript代碼放在Body最下面,且最好是經過外鏈式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!--CSS樣式建議放在head,外部調用--> <link type="text/css" rel="stylesheet" href="xiaomi_box/box_css.css"> </head> <body> <!--HTML頁面內容 --> <!-- 建議放在body最下面,外部調用--> <script type="application/javascript" src="4-1.js"></script> </body> </html>
JavaScript數據類型分爲兩類:原始類型(primitive type)和對象類型(object type)
原始類型:
數字、字符串、布爾值、還有兩個特殊的原始值(null[空值]和undefined[未定義])
對象類型:
一、普通對象(集合-能夠理解爲字典且無序)
對象是屬性的集合每一個屬性都是由"名/值組成" 能夠理解爲Python中的字典擦~ 這個值能夠是原始類型的數字、字符串也能夠是對象
二、全局對象
有一個特殊的對象Global object
三、數組對象(有序的集合能夠理解爲列表)
數組(array),有序的集合能夠理解爲Python中的列表
四、函數對象
首先JavaScript裏面沒有"子類"和"父類"的概念,也沒有"類"(class)和"實例"(instance)的區分,全靠一種很奇特的"原型鏈"(prototype chain)模式,來實現繼承。
重要的事情說三遍:JavaScript中沒有類和實例的概念他是一個純面向對象的語言,你能夠理解爲它的對象是靠一個對象來建立的
網景公司在發明與設計JavaScript的目標,其中很重要的兩點:
1. 簡易版的Java;
2. 簡易,簡易仍是簡易。
Brendan Eich設計JavaScript的時候引入了Java一個很是重要的概念:一切皆對象。既然JavaScript裏面有了對象,那麼設不設計繼承就是困擾Brendan Eich的一個問題,若是真是要設計一個簡易的語言其實能夠不要繼承機制,繼承屬於專業的程序員,可是JavaScript裏那麼多的對象,若是沒有一種機制,他們之間將如何聯繫了,這必然會對編寫程序的可靠性帶來很大的問題,可是引入了繼承又會使用JavaScript變成了完整的面向對象的語言,從而提升了它的門檻,讓不少初學者望而卻步,折中之下,Brendan Eich仍是選擇設計繼承,但毫不是標準的繼承(說道這裏我想起了一樣使用EMCAScript標準設計的語言ActionScript,它裏面就有很完整的繼承,作起來很愜意,我常想這是否是JavaScript之後的趨勢,說不定哪天JavaScript會變的揯更完美寫了?)。折中是指Brendan Eich不打算引入類(class),這樣JavaScript至少看起來不像面向對象的語言了,那麼初學者就不會望而卻步了(這是欺騙啊,進來後倒騰死你,這就是所謂的關門打狗了,並且如今不仍是引入了class嗎,可是這個class實際仍是調用了原型鏈)。
Brendan Eich思考以後,決定借鑑C++和java的new命令,將new命令引入了JavaScript,在傳統的面向對象的語言裏,new 用來構造實例對象,new 會調用構造函數,可是傳統面向對象的語言new 後面的是類,內部機制是調用構造函數(constructor),而Brendan Eich簡化了這個操做,在JavaScript裏面,new 後面直接是構造函數,如是咱們能夠這麼寫一個Person類:
function Person(name) { this.name = name } let personOne = new Person("Brendan Eich"); console.log(personOne.name)
這樣就建立了一個新的實例了。可是new有缺陷。用構造函數生成實例對象是沒法沒法共享屬性和方法,例以下面代碼:
function Person(name) { this.name = name; this.nation = 'USA'; } let person1 = new Person("Brendan Eich"); let preson2 = new Person("Shuai Ge"); person1.nation = "China"; console.log(person1.nation); // China console.log(preson2.nation); // USA
每個實例對象,都有本身的屬性和方法的副本。這不只沒法作到數據共享,也是極大的資源浪費。和JavaScript工廠模式的缺點同樣,過多重複的對象會使得瀏覽器速度緩慢,形成資源的極大的浪費。
考慮到這一點,Brendan Eich決定爲構造函數設置一個prototype屬性,這個屬性都是指向一個prototype對象。下面一句話很重要:全部實例對象須要共享的屬性和方法,都放在這個Prototype對象(原型對象)裏面;那些不須要共享的屬性和方法,就放在構造函數裏面。
實例對象一旦建立,將自動引用prototype對象的屬性和方法。也就是說,實例對象的屬性和方法,分紅兩種,一種是本地的,另外一種是引用的。如是咱們能夠改寫下上面的程序:
function Person(name) { this.name = name; } // 咱們不須要指定prototype對象當咱們建立對象的時候默認會生成 Person.prototype = {nation: "USA"}; let person1 = new Person("Brendan Eich"); let person2 = new Person("Shuai Ge"); console.log(person1.nation); console.log(person2.nation);
當咱們這樣寫程序時候Person.prototype.nation = 'China'; 全部實例化的類的nation都會變成China。
因爲全部的實例對象共享同一個prototype對象,那麼從外界看起來,prototype對象就好像是實例對象的原型,而實例對象則好像"繼承"了prototype對象同樣。prototype只是提供了實現JavaScript繼承的一個很方便的途徑和手段。
五、日期、正則、錯誤 三種有用的對象
從字面上就能夠看出日期是就表明日期的對象,正則表達式對象,還有就是定義了錯誤的對象
總結一下就是:數字、字符串、布爾值、null、undefined、對象(集合-字典)、數組(列表)、函數、日期、正則、錯誤這些對象類型,記住一切皆對象
數字類型
一、JavaScript中的數字是不區分整數和浮點數的,它們默認都是採用浮點數展現
默認ES5是支持16進制的,可是不支持8進制,在ES6中明確了二進制採用[0b或0B],8機制採用[0o或0O]表示
二、數字的+、-、*、/、加減乘除
這些複雜的運算符都是經過Math對象屬性定義的函數和常量實現的
三、二進制浮點數和四捨五入偏差
首先要明確一點在計算機的世界裏計算機只是別0,1,咱們平時看到的任何 在計算機的理解力都是0,1,只是在咱們看前作了一個轉換,有了這個前提咱們來看下
console.log(0.1 + 0.2 ) // 0.30000000000000004
what's FK ~ 什麼鬼?JavaScript採用了IEEE-754表示法基本上現代編程語言都採用的是這個,你覺得其餘語言就會是對的嗎?天真你試試~~
緣由是:
那麼0.1和0.2轉換成二進制分別是, (0.1) => 0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 101 (0.2) => 0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 01 而後對上面的兩個二進制數字作加法,獲得的結果是, 0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101 01 再把這個二進制轉換成十進制,就是咱們前面的結果0.30000000000000004了
有寫語言沒有這個問題說明本質上對其作了封裝~
ES6中也沒有對其進行封裝只提供了一個偏差值~~!
那咱們應該如何解決這個問題呢?兩種方法:
變大求值
咱們把float值放大N倍後爲正數在進行計算
let a = 0.1; let b = 0.2; let ret = (a * 10 + b * 10) / 10; console.log(ret);
字符串求值
let a = 0.1; let b = 0.2; let ret = (a + b).toFixed(1); let newRet = parseFloat(ret); console.log(newRet);
日期類型
JavaScript語言核心包括Date()構造函數,用來建立表示日期和時間對象。這些日期對想的方法爲計算提供了簡單的API
let beforDate = new Date(2011, 0, 1); // 2011年1月1日 let laterDate = new Date(2011, 0, 1, 17, 10, 30); //同一天,當地時間5:10:30pm let nowDate = new Date(); // 當前日期和時間 let elapsed = nowDate - laterDate; //日期減法: 計算時間檢核的毫秒數 laterDate.getFullYear(); // ==> 2011 獲取年份 laterDate.getMonth(); // ==> 0 獲取從0計數的月份 laterDate.getDay(); // ==> 5 獲得星期幾,0表明星期日,5表明星期一 laterDate.getHours(); // ==> 17:5pm 當地時間 laterDate.getUTCHours(); // 使用UTC表示小時的時間,基於時區
還有不少方法能夠查看文檔
字符串
字符串和Python中的字符串差很少ES6中增強了Unicode表示法,只要將碼點放入大括號,就能正確解讀該字符。
"\u{20BB7}" // "𠮷" "\u{41}\u{42}\u{43}" // "ABC" let hello = 123; hell\u{6F} // 123 '\u{1F680}' === '\uD83D\uDE80' // true
JavaScript的字符串和Python中的字符串操做有不少類似之處也僅限於類似。
let s = 'hello world'; // 建議JavaScript字符串使用單引號由於在HTML中CSS樣式類的應用是使用雙引號的 s.charAt(0); // ==> 'h' 第一個字符 s.charAt(s.length-1); // ==> 'd' 最後一個字符 s.substring(1, 4); // ==> 獲取第1~4個字符相似列表切片 s.slice(1, 4); // ==> 同上 s.slice(-3); // ==> 'rld' 最後三個字符 s.indexOf('l'); // ==> '2' 獲取字符第一次出現的位置 s.split(','); // ==> 分割成數組 s.replace('h', 'H'); // ==> 'Hello World' 全文字符替換 s.toUpperCase(); // ==> 'HELLO WORLD' 轉爲爲答謝
一樣和Python中同樣字符串是隻讀的若是想修改,只能新建一個新的
JavaScript能夠理解爲只讀數組也能夠用數組的部分功能好比s[0]下標~~
而且JavaScript自己支持正則匹配功能~
let text = 'testing: 1, 2, 3'; // 文本示例 let pattern = /\d+/g; // 匹配全部包含一個或多個數字的實例 pattern.test(text); // 首先pattern定義了一個匹配方法,而後匹配的字符是text而後返回結果 基本上全部語言的匹配都大同小異 text.search(pattern); // ==> 9 首次匹配成功的位置 text.match(pattern); // ==> ["1", "2", "3"] 全部匹配組成的數組 text.replace(pattern, "#"); // ==> 'testing: #, #, #' text.split(/\D+/); // ==> ["", "1", "2", "3"] 用非數字字符截取字符串
布爾值
True 或者False和Python中同樣
null和undefind
null
JavaScript中的null它表示一個特殊值,經常使用來描述「空值」。null在javaScript中也是一個特殊的對象,它能夠表示數字、字符串、對象是無值的
undefind
JavaScript還有第二個值來表示值的空缺,可是他表示更深層次的空值,表示變量未初始化或者未定義
undefind通常能夠理解爲出乎意料的的系統級別的空值,而null表示正常的意料中的空值,雖然它倆不一樣可是大多狀況下能夠互換的,可是在定義變量或者傳參的時候建議使用null
全局對象
在原始類型和對象類型中,對象類型有個很是重要的對象:全局對象 全局對象定義的屬性是全局定義的符號,JavaScript能夠直接調用
JavaScript解釋器啓動的時候(或者頁面瀏覽器加載新頁面的時候),它將建立一個新的全局對象,並給它一組定義的初始屬性
客戶端window對象額外定義了一些全局對象,而且在代碼最頂級-不在任何函數的JavaScript能夠經過this關鍵字引用let global = this;
包裝對象
JavaScript對象是一種複合值:他是屬性或已命名值的集合經過 「.」【點】來調用屬性值,當屬性值是一個函數的時候稱其爲「方法」
咱們來看下面一個例子:
let s = 'hello world';
s.substring(1,4)
在JavaScript中字符串不是一個對象那爲何他又屬性和方法呢?
本質是:字符串在調用屬性的時候會將字符串的值經過調用new string(s)的方式轉換爲對象,一旦引用結束就個被建立的對象就被銷燬(其實在實現上並不必定銷燬這個臨時對象,整個過程看起來是這個樣子)個人天啊~~
包括數字、和布爾值都是同理~ Number()或Boolean
"==" 等於運算將原始值和包裝對象視爲相等,單「===」全等運算將它們視爲不等
不可變原始值和可變的對象引用
JavaScript中原始值(字符串、數字、null、undefind、布爾值)與對象(包括數組和函數)有本質的區別,原始值不能夠修改好比9這個數你怎麼改爲其餘值這自己就說不通9就是9他不能修改
字符串就不太明顯了,能夠用Python的理解,一樣不能夠修改,字符串是一段連續的內存地址
對於對象來講他們是可變的可修改的,而且即便兩個對象的屬性及其相同的值他們也是不相等的,各索引元素徹底相等的兩個數組也不相同
let arrayA1 = [1, 2, 3, 4] let arrayB1 = [1, 2, 3, 4] arrayA1 == arrayB1 false
我擦這是什麼鬼?這個咱們能夠理解爲咱們建立了兩個對象在內存中,兩個不一樣的內存地址怎麼能相等呢?我覺的JavaScript在這裏更精準些
咱們一般將對象稱爲引用類型(reference type) 今後來和JavaScript的類型區分開,依照術語對象之都是引用,這個「引用能夠理解爲對內存地址的引用」
let arrayA = []; //定義一個空的數組對象 let arrayB = arrayA; // 把arrayA的值賦值爲arrayB arrayA[0] = 1; //給arrayA里加一個元素由於A和B一樣指向的是一塊內存因此B也隨之修改 arrayA === arrayB true
上面是鐵則化簡就是:只有兩個值引用的內存地址相同才相等
因此若是引用同一個內存地址那麼一個值修改的時候另外一個值將一樣修改,有時候你只想複製一份能夠經過循環來取
a = [1, 2, 3, 4, 5, 'hello'] b = [] for(let i = 0; i <a.length; i++){ b[i] = a[i]}
複製完也是不一樣的由於兩個是不一樣的對象須要注意
類型轉換
這個裏面須要注意的是:
相等性:
null == undefind 是相等的
"0" == 0 在比較以前將字符串轉換爲數字
0 == false 比較以前將布爾值轉換爲數字
「0」 == false 在比較前將布爾值和字符串轉化爲數字
顯示類型轉換
除了布爾值和undefind以外的任何值都具備toString()方法這個方法一般和string()方法返回結果一致
// 二進制轉換 let n = 17 binary_string = n.toString(2) //轉換爲二進制 octal_string = "0" + n.toString(8) //轉化爲8進制 hex_string = "0x" + n.toString(16) //轉化爲16進制
對象轉換爲原始值
({x: 1, x2: 2}).toString() "[object Object]" [1, 2, 3].toString() "1,2,3" (function(x){f(x);}).toString() "function (x){f(x);}" /\d+/g.toString() "/\d+/g" new Date(2017,9,12).toString() "Thu Oct 12 2017 00:00:00 GMT+0800 (中國標準時間)"
這個結果頗有意思~~
JavaScript對象到字符串轉化的過程以下:
一、首先判斷是否有toString()方法若是他返回一個原始值將這個值轉化爲字符串返回
二、若是沒有toString()方法或者返回的不是一個原始值則調用則掉用valueOf()方法,若是存在這個方法則調用它,若是返回的是原始值則轉化爲字符串
三、不然javaScript沒法從toString()或valueOf()方法中得到一個原始值則拋出異常
在對象到數字的過程當中作了以下操做
一、若是對象存在valueOf()方法調用他若是返回一個原始值將這個原始值轉換爲數字(若是須要的話)而後返回數字
二、不然若是具備toString()方法,後者返回一個原始值JavaScript將它轉換並返回
三、異常
對象轉換爲數字的細節解釋了爲何 空數組會被轉化爲數字0數組默認繼承了valueOf()這個方法返回一個對象在調用toString()方法轉換爲空字符,空字符在轉化爲數字,JavaScript 「+」和「==」若是一個是對象JavaScript會將方法轉化爲原始值而不是其餘素數運算符的方法執行對象到數字的轉換
日期的狀況比較特殊它是JavaScript語言核心惟一的預先定義類型,他定義了有意義的到字符串和數字的轉換經過valueOf或toString()返回原始值將被直接使用而不是強制轉換爲數字或字符串
let塊級做用於
ES6 新增了let
命令,用來聲明變量。它的用法相似於var
,可是所聲明的變量,只在let
命令所在的代碼塊內有效。
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1
因此for循環很適合let
for (let i = 0; i < 10; i++) { // ... } console.log(i); // ReferenceError: i is not defined
上面代碼中,計數器i
只在for
循環體內有效,在循環體外引用就會報錯。
下面的代碼若是使用var
,最後輸出的是10
。
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10
上面代碼中,變量i
是var
命令聲明的,在全局範圍內都有效,因此全局只有一個變量i
。每一次循環,變量i
的值都會發生改變,而循環內被賦給數組a
的函數內部的console.log(i)
,裏面的i
指向的就是全局的i
。也就是說,全部數組a
的成員裏面的i
,指向的都是同一個i
,致使運行時輸出的是最後一輪的i
的值,也就是10。
若是使用let
,聲明的變量僅在塊級做用域內有效,最後輸出的是6。
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
不容許使用提早
在ES5咱們調用1個變量的時候若是他沒有聲明默認是undefind,如今不能夠了沒有聲明的變量不容許使用
暫時性死區
只要塊級做用域內存在let
命令,它所聲明的變量就「綁定」(binding)這個區域,再也不受外部的影響。
var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; }
這裏也覺得typeof將不是一個安全操做由於它會觸發異常了若是變量沒有聲明
不容許重複聲明在相同做用於塊內
function func(arg) { let arg; // 報錯 } function func(arg) { { let arg; // 不報錯 } }
爲何要引入塊級做用域?
ES5 只有全局做用域和函數做用域,沒有塊級做用域,這帶來不少不合理的場景。
第一種場景,內層變量可能會覆蓋外層變量。
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } f(); // undefined
上面代碼的原意是,if
代碼塊的外部使用外層的tmp
變量,內部使用內層的tmp
變量。可是,函數f
執行後,輸出結果爲undefined
,緣由在於變量提高,致使內層的tmp
變量覆蓋了外層的tmp
變量。
第二種場景,用來計數的循環變量泄露爲全局變量。
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
const
聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
這意味着這個常量一旦聲明就必須馬上賦值之後不容許修改
ES6中有了塊級做用域,JavaScript的做用域鏈基本上和Python的做用域類似了 全局和局部做用域
特殊的是塊級做用於的暫時性死區
直接定義原始類型也就是直接聲明
let a = 1; let b = "hello" let c = null let d; // undefind
對象和數組的初始化表達式也就是直接聲明對象和數組
let a = {}
let b =[]
函數定義表達式也就是聲明一個函數
let function a { console.log("Hello World~ ")}
屬性的訪問
普通對象{} 能夠經過.或者對象[‘key’]來進行訪問,數組對象的話能夠相似python的列表訪問方式
若是屬性對應的值是一個方法的話能夠加()執行
對象建立表達式
let a = new Object()
加、減、乘、除、位運算
「==」和「===」相等和不等運算
== 和 === 、 == 爲寬鬆的判斷是否相等, === 爲嚴格的判斷是否相等, === 在判斷的時候不會作類型的轉換!!!
他是如何嚴格判斷的呢?
不嚴格的==是如何判斷的?
一、若是兩個操做數類型相等,那麼就相似嚴格相等
2.、若是操做兩個數類型不一樣可能相等:
比較運算符相似==可能會進行類型轉換
instancefo相似Python中的isinstance
let nowDate = new Date() undefined nowDate instanceof Date true
Python的是
print(isinstance(11, int))
條件運算符JavaScript惟一一個三元運算符 ?
表達式: bool ? true: false 解釋:問號前面爲判斷式,若是爲真用 冒號前的true對應的表達式值計算而後返回,不然用冒號後面的false表達式計算並返回
// username須要提早定義ES6已經不容許未定義使用了 greeting = "hello" + ( username ? username : "there") // 等價於 let greeting = "hello"; if (username){ console.log(greeting + username); }else{ console.log(greeting + "there"); }
其餘運算符
一、typeof獲取類型
二、delete刪除屬性和python中的相似
三、逗號運算符哈哈
逗號運算符是二元運算符,它的操做數能夠是任意類型。它首先計算左操做數,而後計算右操做數,最後返回右操做數的值,看下面的代碼
i = 0, j = 1, k = 2 // 最後返回的結果是 2,它和線面的代碼基本上是等價的 i = 0; j = 1; k =2
用在哪裏?
// for循環中的第一個逗號是第let語句的一部分 // 第二個逗號是逗號運算符 // 它將兩個表達式(i++和j--)放在一條for循環中 for(let i = 0, j = 10; i <j ; i++, j--){ console.log(i + j) }