整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          使用nodejs和express搭建http web服務

          nodejs作為一個優秀的異步IO框架,其本身就是用來作為http web服務器使用的,nodejs中的http模塊,提供了很多非常有用的http相關的功能。

          雖然nodejs已經帶有http的處理模塊,但是對于現代web應用程序來說,這或許還不太夠,于是我們有了express框架,來對nodejs的內容進行擴展。

          今天我們將會介紹一下使用nodejs和express來開發web應用程序的區別。

          使用nodejs搭建HTTP web服務

          nodejs提供了http模塊,我們可以很方便的使用http模塊來創建一個web服務:

          const http = require('http')
          
          const hostname = '127.0.0.1'
          const port = 3000
          
          const server = http.createServer((req, res) => {
            res.statusCode = 200
            res.setHeader('Content-Type', 'text/plain')
            res.end('welcome to www.flydean.com\n')
          })
          
          server.listen(port, hostname, () => {
            console.log(`please visit http://{hostname}:{port}/`)
          })
          

          上面創建的http服務監聽在3000端口。我們通過使用createServer方法來創建這個http服務。

          該方法接受一個callback函數,函數的兩個參數分別是 req (http.IncomingMessage 對象)和一個res(http.ServerResponse 對像)。

          在上面的例子中,我們在response中設置了header和body值,并且以一個end方法來結束response。

          請求nodejs服務

          我們創建好http web服務之后,一般情況下是從web瀏覽器端進行訪問和調用。但是我們有時候也需要從nodejs后端服務中調用第三方應用的http接口,下面的例子將會展示如何使用nodejs來調用http服務。

          先看一個最簡單的get請求:

          const http = require('http')
          const options = {
            hostname: 'www.flydean.com',
            port: 80,
            path: '/',
            method: 'GET'
          }
          
          const req = http.request(options, res => {
            console.log(`status code: ${res.statusCode}`)
          
            res.on('data', d => {
              console.log(d);
            })
          })
          
          req.on('error', error => {
            console.error(error)
          })
          
          req.end()
          

          上面代碼我們使用了http.request來創建一個request,并且傳入了我們自帶的options參數。

          我們通過res的回調事件來進行相應的處理。

          再看一個簡單的post請求:

          const http = require('http')
          
          const data = JSON.stringify({
            name: 'flydean'
          })
          
          const options = {
            hostname: 'www.flydean.com',
            port: 80,
            path: '/',
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              'Content-Length': data.length
            }
          }
          
          const req = http.request(options, res => {
            console.log(`status code: ${res.statusCode}`)
          
            res.on('data', d => {
              console.log(d);
            })
          })
          
          req.on('error', error => {
            console.error(error)
          })
          
          req.write(data)
          req.end()
          

          post和get相似,不同的是options中的method不一樣,同時put可以有多種請求類型,所以我們需要在headers中指定。

          同樣的,PUT 和 DELETE 也可以使用同樣的方式來調用。

          第三方lib請求post

          直接使用nodejs底層的http.request有點復雜,我們需要自己構建options,如果使用第三方庫,比如axios可以讓post請求變得更加簡單:

          const axios = require('axios')
          
          axios
            .post('http://www.flydean.com', {
              name: 'flydean'
            })
            .then(res => {
              console.log(`status code: ${res.statusCode}`)
              console.log(res)
            })
            .catch(error => {
              console.error(error)
            })
          

          上面的例子中,我們直接使用axios的post請求,并將請求結果封存成了promise,然后通過then和catch來進行相應數據的處理。非常的方便。

          獲取http請求的正文

          在上面的例子中,我們通過監聽req的data事件來輸出http請求的正文:

            res.on('data', d => {
              console.log(d);
            })
          })
          

          這樣做其實是有問題的,并不一定能夠獲得完整的http請求的正文。

          因為res的on data事件是在服務器獲得http請求頭的時候觸發的,這個時候請求的正文可能還沒有傳輸完成,換句話說,請求回調中的request是一個流對象。

          我們需要這樣處理:

          const server = http.createServer((req, res) => {
            let data = []
            req.on('data', chunk => {
              data.push(chunk)
            })
            req.on('end', () => {
              console.log(JSON.parse(data));
            })
          })
          

          當每次觸發data事件的時候,我們將接受到的值push到一個數組里面,等所有的值都接收完畢,觸發end事件的時候,再統一進行輸出。

          這樣處理顯然有點麻煩。

          我們介紹一個在express框架中的簡單方法,使用 body-parser 模塊:

          const bodyParser = require('body-parser')
          
          app.use(
            bodyParser.urlencoded({
              extended: true
            })
          )
          
          app.use(bodyParser.json())
          
          app.post('/', (req, res) => {
            console.log(req.body)
          })
          

          上面的例子中,body-parser對req進行了封裝,我們只用關注與最后的結果即可。

          Express和使用express搭建http web服務

          express是什么呢?

          express是基于 Node.js 平臺,快速、開放、極簡的 web 開發框架。它提供一系列強大的特性,幫助你創建各種 Web 和移動設備應用。

          豐富的 HTTP 快捷方法和任意排列組合的 Connect 中間件,讓你創建健壯、友好的 API 變得既快速又簡單。

          Express 不對 Node.js 已有的特性進行二次抽象,我們只是在它之上擴展了 Web 應用所需的基本功能。

          express helloworld

          我們看一下怎么使用Express來搭建一個helloworld:

          var express = require('express');
          var app = express();
          app.get('/', function (req, res) {
            res.send('Hello World!');
          });
          var server = app.listen(3000, function () {
            var host = server.address().address;
            var port = server.address().port;
            console.log('Example app listening at http://%s:%s', host, port);
          });
          
          

          簡單的使用app.listen即可搭建好一個http web服務。

          express路由

          有了web服務,我們需要對不同的請求路徑和請求方式進行不同的處理,這時候就需要使用到了express路由功能:

          // 對網站首頁的訪問返回 "Hello World!" 字樣
          app.get('/', function (req, res) {
            res.send('Hello World!');});
          // 網站首頁接受 POST 請求
          app.post('/', function (req, res) {
            res.send('Got a POST request');});
          // /user 節點接受 PUT 請求
          app.put('/user', function (req, res) {
            res.send('Got a PUT request at /user');});
          // /user 節點接受 DELETE 請求
          app.delete('/user', function (req, res) {
            res.send('Got a DELETE request at /user');});
          

          更高級一點的,我們還可以在請求路徑中做路由匹配:

          // 匹配 acd 和 abcd
          app.get('/ab?cd', function(req, res) {
            res.send('ab?cd');});
          // 匹配 abcd、abbcd、abbbcd等
          app.get('/ab+cd', function(req, res) {
            res.send('ab+cd');
          });
          // 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
          app.get('/ab*cd', function(req, res) {
            res.send('ab*cd');
          });
          // 匹配 /abe 和 /abcde
          app.get('/ab(cd)?e', function(req, res) {
           res.send('ab(cd)?e');});
          
          // 匹配任何路徑中含有 a 的路徑:
          app.get(/a/, function(req, res) {
            res.send('/a/');
          });
          
          // 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等
          app.get(/.*fly/, function(req, res) {
            res.send('/.*fly/');
          });
          
          

          Express 路由句柄中間件

          有時候,一個請求可能有多個處理器,express提供了路由句柄(中間件)的功能,我們可自由組合處理程序。

          注意,在路由句柄中,我們需要調用next方法,來觸發下一個路由方法。

          var cb0 = function (req, res, next) {
            console.log('CB0');
            next();}
          var cb1 = function (req, res, next) {
            console.log('CB1');
            next();}
          app.get('/example/d', [cb0, cb1], function (req, res, next) {
            console.log('response will be sent by the next function ...');
            next();
          }, function (req, res) {
            res.send('Hello from D!');
          });
          

          上面的請求會經過cb0,cb1和自定義的兩個function,最終結束。

          Express 響應方法

          express提供了很多響應方法API,可以方便我們的代碼編寫:

          方法描述res.download()提示下載文件。res.end()終結響應處理流程。res.json()發送一個 JSON 格式的響應。res.jsonp()發送一個支持 JSONP 的 JSON 格式的響應。res.redirect()重定向請求。res.render()渲染視圖模板。res.send()發送各種類型的響應。res.sendFile以八位字節流的形式發送文件。res.sendStatus()設置響應狀態代碼,并將其以字符串形式作為響應體的一部分發送。

          Express 的靜態資源

          通常來說,靜態資源是不需要服務端進行處理的,在express中,可以使用express.static來指定靜態資源的路徑:

          app.use(express.static('public'));
          現在,public 目錄下面的文件就可以訪問了。
          http://localhost:3000/images/kitten.jpg
          http://localhost:3000/css/style.css
          http://localhost:3000/js/app.js
          http://localhost:3000/images/bg.png
          http://localhost:3000/hello.html
          //多個靜態資源目錄
          app.use(express.static('public'));
          app.use(express.static('files'));
          //靜態前綴
          app.use('/static', express.static('public'));
          http://localhost:3000/static/images/kitten.jpg
          http://localhost:3000/static/css/style.css
          

          Express 使用模板引擎

          web應用當然需要html文件,express中可以使用多種模板語言,讓編寫html頁面更加容易。如果想要使用模板引擎。我們可以使用下面的步驟:

          1. views, 放模板文件的目錄,比如: app.set(‘views’, ‘./views’)
          2. view engine, 模板引擎,比如: app.set(‘view engine’, ‘jade’)
          3. 在 views 目錄下生成名為 index.jade 的 Jade 模板文件,內容如下:
          html
            head
              title!= title
            body
              h1!= message
          
          1. 在nodejs服務端配置route規則
          //配置route 規則
          app.get('/', function (req, res) {
            res.render('index', { title: 'Hey', message: 'Hello there!'});
          });
          

          總結

          nodejs和express是非常方便的http web服務框架,希望大家能夠喜歡。

          本文作者:flydean程序那些事

          本文鏈接:http://www.flydean.com/nodejs-http-express/

          本文來源:flydean的博客

          歡迎關注我的公眾號:「程序那些事」最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

          、 模板引擎的介紹

          在一個web應用程序中,如果只是使用服務器端代碼來編寫客戶端html代碼,前后端不分離,那么會造成很大的工作量,而且寫出來的代碼會比較難以閱讀和維護。如果只是使用客戶端的靜態的HTML文件,那么后端的邏輯也會比較難以融入到客戶端的HTML代碼中。

          為了便于維護,且使后端邏輯能夠比較好的融入前端的HTML代碼中,同時便于維護,很多第三方開發者就開發出了各種Nodejs模板引擎,其中比較常用的就是Jade模板引擎和Ejs 模板引擎。

          二、 Jade模板引擎的使用方法

          首先,需要使用包管理工具npm下載,下載的方式非常的簡單,如下:

          npm install jade

          下載完成之后,因為我們的模板文件放在目錄views里面,可以通過view engine內部變量值設定為”jade ”的方式,來使用jade模板引擎,設置代碼如下:

          app.set(‘view engine’, ‘jade’ );

          默認情況下,express引用程序中的所有視圖文件都是在項目根目錄的views文件夾里面的,可以通過設置views內部變量的值的方法顯示指定視圖文件的存放路徑,方法如下:

          app.set(‘views’, ——dirname+’/views’)

          那么,在express框架中,響應服務器的請求的時候,可以使用響應對象的方法render來渲染視圖,返回給客戶端,代碼如下:

          res.render(view, [locals], [callback])

          方法里面有三個參數,view參數就是模板視圖的文件的名字,是一個字符串,指定需要返回給客戶端的模板視圖,locals參數值為一個對象,用于指定視圖所用的數據的變量的值或者名字,callback為回調函數,其中local和callback兩個參數是可選參數。例如

          res.render(‘index’,{data:‘這里是數據對象的值’})

          三、Ejs模板引擎的使用方法

          接下來我們介紹一下ejs模板引擎的使用方法,首先,同樣的需要使用npm包管理工具安裝esj模板引擎,代碼如下

          npm install ejs

          下載了EJS模板引擎之后,可以在整個項目的根目錄下的views文件 中,創建擴展名字為ejs的模板文件,模板文件的寫法和HTML的寫法大致相同,沒有太大的區別,輸出表達式寫成

          <%s=輸出表達式%>

          可以像在php文件或者asp.net文件中一樣,混寫服務器端nodejs腳本,攪拌寫在<%腳本語言%>中

          同樣的,在服務器端接收到客戶端的請求后,在express框架的web應用程序中,可以通過render方法返回給html,示例代碼如下:

          res.render(‘index’, {data : ‘這里是數據的值得’})

          第一個參數為模板,可以加后綴如:index.ejs,或者省略,第二個參數為數據對象,那么我們用數據對象的名字變量 ,去寫模板視圖,那么響應給客戶端的就是一個完整的html。

          四、 總結

          除了以上兩個模板引擎以外,nodejs還有很多的模板引擎可以使用,例如Haml模板引擎、CoffeeKup模板引擎以及jQuery模板引擎等等,只是我們介紹的這兩種是現在比較主流的,使用的比較多的,其他的用法差異并不是非常的大,要學會舉一反三,當然,我們只是初略的介紹了jade模板引擎和ejs模板引擎的用法,想要熟練的掌握,還得需要自行寫代碼聯系驗證,畢竟,實踐出真知。

          感謝源碼時代H5學科講師提供此文章!

          本文為原創文章,轉載請注明出處(http://www.itsource.cn)!

          .javaScript的數據類型有什么

          基本數據類型:Undefined、Null、Boolean、Number、String、Object、Symbol(es6--原始數據類型)

          復雜數據類型:object,array,function

          區別:基本數據類型把數據名和值直接存儲在棧當中;復雜數據類型在棧中存儲數據名和一個堆的地址,在堆中存儲屬性及值,訪問時先從棧中獲取地址,再到堆中拿出相應的值

          2.檢測數據類型有什么方法

          typeof

          typeof xxx得到的值有以下幾種類型:undefined boolean number string object function、symbol ,比較簡單,不再一一演示了。
          這里需要注意的有三點:

          • typeof null結果是object ,實際這是typeof的一個bug,null是原始值,非引用類型
          • typeof [1, 2]結果是object,結果中沒有array這一項,引用類型除了function其他的全部都是object
          • typeof Symbol() 用typeof獲取symbol類型的值得到的是symbol,這是 ES6 新增的知識點

          instanceof

          用于實例和構造函數的對應。例如判斷一個變量是否是數組,使用typeof無法判斷,但可以使用[1, 2] instanceof Array來判斷。因為,[1, 2]是數組,它的構造函數就是Array。同理:

          function Foo(name) { 
             this.name = name 
          } 
          var foo = new Foo('bar’) 
          console.log(foo instanceof Foo) // true
          

          3.介紹js有哪些內置對象?

          Object 是 JavaScript 中所有對象的父對象
          數據封裝類對象:Object、Array、Boolean、Number 和 String
          其他對象:Function、Arguments、Math、Date、RegEx、Error

          4.如何區分數組和對象?

          (1)從原型入手,Array.prototype.isPrototypeOf(obj); 利用isPrototypeOf()方法,判定Array是不是在obj的原型鏈中,如果是,則返回true,否則false。Array.prototype.isPrototype([]) //true
          (2)也可以從構造函數入手,利用對向的constructor屬性
          (3)根據對象的class屬性(類屬性),跨原型鏈調用toString()方法。Object.prototype.toString.call(Window);
          (4)Array.isArray()方法。

          5.null,undefined 的區別?

          null 表示一個對象被定義了,值為“空值”;
          undefined 表示不存在這個值。

          typeof undefined //"undefined"
          undefined :是一個表示"無"的原始值或者說表示"缺少值",就是此處應該有一個值,但是還沒有定義。當嘗試讀取時會返回 undefined;
          例如變量被聲明了,但沒有賦值時,就等于undefined

          typeof null //"object"
          null : 是一個對象(空對象, 沒有任何屬性和方法);
          例如作為函數的參數,表示該函數的參數不是對象;

          注意:

          在驗證null時,一定要使用 === ,因為 == 無法分別 null 和 undefined
          undefined表示"缺少值",就是此處應該有一個值,但是還沒有定義。典型用法是:
          1)變量被聲明了,但沒有賦值時,就等于undefined。
          2) 調用函數時,應該提供的參數沒有提供,該參數等于undefined。
          3)對象沒有賦值的屬性,該屬性的值為undefined。
          4)函數沒有返回值時,默認返回undefined。

          null表示"沒有對象",即該處不應該有值。

          典型用法是:
          1) 作為函數的參數,表示該函數的參數不是對象。
          2) 作為對象原型鏈的終點。

          6.聲明變量和聲明函數的提升有什么區別?

          (1) 變量聲明提升:變量申明在進入執行上下文就完成了。
          只要變量在代碼中進行了聲明,無論它在哪個位置上進行聲明, js引擎都會將它的聲明放在范圍作用域的頂部;
          (2) 函數聲明提升:執行代碼之前會先讀取函數聲明,意味著可以把函數申明放在調用它的語句后面。
          只要函數在代碼中進行了聲明,無論它在哪個位置上進行聲明, js引擎都會將它的聲明放在范圍作用域的頂部;
          (3) 變量or函數聲明:函數聲明會覆蓋變量聲明,但不會覆蓋變量賦值。
          同一個名稱標識a,即有變量聲明var a,又有函數聲明function a() {},不管二者聲明的順序,函數聲明會覆蓋變量聲明,也就是說,此時a的值是聲明的函數function a() {}。注意:如果在變量聲明的同時初始化a,或是之后對a進行賦值,此時a的值變量的值。eg: var a; var c = 1; a = 1; function a() { return true; } console.log(a);

          原型,原型鏈

          1.JavaScript原型,原型鏈 ? 有什么特點?

          原型

          每個對象都會在其內部初始化一個屬性,就是prototype(原型)
          使用hasOwnProperty() 可以判斷這個屬性是不是對象本身的屬性

          問題:Javascript中,有一個函數,執行時對象查找時,永遠不會去查找原型,這個函數是?
          hasOwnProperty
          javaScript中hasOwnProperty函數方法是返回一個布爾值,指出一個對象是否具有指定名稱的屬性。此方法無法檢查該對象的原型鏈中是否具有該屬性;該屬性必須是對象本身的一個成員。

          使用方法:
          object.hasOwnProperty(proName)
          其中參數object是必選項。一個對象的實例。
          proName是必選項。一個屬性名稱的字符串值。

          如果 object 具有指定名稱的屬性,那么JavaScript中hasOwnProperty函數方法返回 true,反之則返回 false。

          原型鏈

          當我們在訪問一個對象的屬性時,如果這個對象內部不存在這個屬性,那么他就會去prototype里找這個屬性,這個prototype又會有自己的prototype,于是就這樣一直找下去,找到Object.__proto__為止,找不到就返回unde也就是我們平時所說的原型鏈的概念。
          關系:instance.constructor.prototype = instance.__proto__
          特點:
          JavaScript對象是通過引用來傳遞的,我們創建的每個新對象實體中并沒有一份屬于自己的原型副本。當我們修改原型時,與之相關的對象也會繼承這一改變。
          當我們需要一個屬性的時,Javascript引擎會先看當前對象中是否有這個屬性, 如果沒有的話,就會查找他的Prototype對象是否有這個屬性,如此遞推下去,一直檢索到 Object 內建對象。

          所有的引用類型(數組、對象、函數),都具有對象特性,即可自由擴展屬性(null除外)
          所有的引用類型(數組、對象、函數),都有一個__proto__屬性,屬性值是一個普通的對象
          所有的函數,都有一個prototype屬性,屬性值也是一個普通的對象
          所有的引用類型(數組、對象、函數),__proto__屬性值指向它的構造函數的prototype屬性值

          原型鏈中的this

          所有從原型或更高級原型中得到、執行的方法,其中的this在執行時,就指向了當前這個觸發事件執行的對象。

          閉包

          閉包的形成與變量的作用域以及變量的生存周期有密切的關系

          1.變量的作用域

          • 在js中我們把作用域分為全局作用域和局部作用域,全局作用域就是window,在沒有塊級作用域概念的時候,每一個函數都是一個局部作用域。
          • 其實變量的作用域,就說指變量的有效范圍。我們最長說的就是在函數中聲明的變量作用域。
          • 當在函數中聲明一個變量的時候,如果改變量沒有用var關鍵字去定義那么該變量就是一個全局變量,但是這樣做最容易造成命名沖突。
          • 另一種情況就是使用var聲明的變量,這時候的變量就是局部變量,只有在該函數內部可以訪問,在函數外面是訪問不到的
          • 在javascript中,函數可以用來創造函數作用域。在函數中搜索變量的時候,如果該函數當中沒有這個變量,那么這次搜索過程會隨著代碼執行環境創建的作用域鏈往外層逐層搜索,一直搜索到window對象為止,找不到就會拋出一個為定義的錯誤。而這種從內到外逐層查找的關系在js中我們稱為作用域鏈

          2.變量的生存周期

          除了變量作用域之外,另外一個跟閉包有關的概念就是變量的生存周期,對于全局變量來說,全局變量的生存周期是永久的,除非我們主動銷毀這個全局變量,而對于函數內部的使用var聲明的變量來說,當退出函數是,這些變量就會隨著函數的結束而銷毀。

          3.閉包的形成

          Javascript允許使用內部函數,可以將函數定義和函數表達式放在另一個函數的函數體內。而且,內部函數可以訪問它所在的外部函數聲明的局部變量、參數以及聲明的其他內部函數。當其中一個這樣的內部函數在包含它們的外部函數之外被調用時,就會形成閉包。常見的閉包寫法就是簡單的函數套函數,通過另一個函數訪問這個函數的局部變量,利用閉包可以突破作用域鏈,將函數內部的變量和方法傳遞到外部,延續變量的生命。使用閉包可以減少全局環境的污染,也可用延續變量的生命。

          4.閉包的適用場景

          閉包的適用場景非常廣泛,首先從閉包的優點出發就是:
          減少全局環境的污染生成獨立的運行環境
          模塊化就是利用這個特點對不同的模塊都有自己獨立的運行環境,不會和全局沖突,模塊和模塊之間通過拋出的接口進行依賴使用
          以及像我們常用的jquery類庫(避免和全局沖突使用閉包實現自己獨立的環境)

          可以通過返回其他函數的方式突破作用域鏈
          可以利用這個功能做一些值的緩存工作,例如常見的設計模式(單例模式),以及現在比較火的框架vue中的計算屬性

          其實當遇到以下場景的時候都可以使用閉包
          1) 維護函數內的變量安全,避免全局變量的污染。
          2) 維持一個變量不被回收。
          3) 封裝模塊

          5.閉包的缺點

          由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大。所以在閉包不用之后,將不使用的局部變量刪除,使其被回收。在IE中可能導致內存泄露,即無法回收駐留在內存中的元素,這時候需要手動釋放。

          6.內存泄露

          內存泄漏指一塊被分配的內存既不能使用,又不能回收,直到瀏覽器進程結束。

          出現原因:

          1) 循環引用:含有DOM對象的循環引用將導致大部分當前主流瀏覽器內存泄露。循環 引用,簡單來說假如a引用了b,b又引用了a,a和b就構成了循環引用。
          2) JS閉包:閉包,函數返回了內部函數還可以繼續訪問外部方法中定義的私有變量。
          3) Dom泄露,當原有的DOM被移除時,子結點引用沒有被移除則無法回收。

          7.JavaScript垃圾回收機制

          Javascript中,如果一個對象不再被引用,那么這個對象就會被GC(garbage collection)回收。如果兩個對象互相引用,而不再被第3者所引用,那么這兩個互相引用的對象也會被回收。垃圾回收不是時時的,因為其開銷比較大,所以垃圾回收器會按照固定的時間間隔周期性的執行。

          函數a被b引用,b又被a外的c引用,這就是為什么函數a執行后不會被回收的原因。

          8.垃圾回收的兩個方法:

          標記清除法:

          1) 垃圾回收機制給存儲在內存中的所有變量加上標記,然后去掉環境中的變量以及被環境中變量所引用的變量(閉包)。
          2) 操作1之后內存中仍存在標記的變量就是要刪除的變量,垃圾回收機制將這些帶有標記的變量回收。

          引用計數法:

          1) 垃圾回收機制給一個變量一個引用次數,當聲明了一個變量并將一個引用類型賦值給該變量的時候這個值的引用次數就加1。
          2) 當該變量的值變成了另外一個值,則這個值得引用次數減1。
          3) 當這個值的引用次數變為0的時候,說明沒有變量在使用,垃圾回收機制會在運行的時候清理掉引用次數為0的值占用的空間。

          JS運行機制

          JavaScript引擎是單線程運行的,瀏覽器無論在什么時候都只且只有一個線程在運行JavaScript程序.瀏覽器的內核是多線程的,它們在內核制控下相互配合以保持同步,一個瀏覽器至少實現三個常駐線程:javascript引擎線程,GUI渲染線程,瀏覽器事件觸發線程。這些異步線程都會產生不同的異步的事件.

          1) javascript引擎是基于事件驅動單線程執行的,JS引擎一直等待著任務隊列中任務的到來,然后加以處理,瀏覽器無論什么時候都只有一個JS線程在運行JS程序。
          2) GUI渲染線程負責渲染瀏覽器界面,當界面需要重繪(Repaint)或由于某種操作引發回流(reflow)時,該線程就會執行。但需要注意 GUI渲染線程與JS引擎是互斥的,當JS引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到JS引擎空閑時立即被執行。
          3) 事件觸發線程,當一個事件被觸發時該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理。這些事件可來自JavaScript引擎當前執行的代碼塊如setTimeOut、也可來自瀏覽器內核的其他線程如鼠標點擊、AJAX異步請求等,但由于JS的單線程關系所有這些事件都得排隊等待JS引擎處理。(當線程中沒有執行任何同步代碼的前提下才會執行異步代碼)

          當程序啟動時, 一個進程被創建,同時也運行一個線程, 即為主線程,js的運行機制為單線程

          程序中跑兩個線程,一個負責程序本身的運行,作為主線程; 另一個負責主線程與其他線程的的通信,被稱為“Event Loop 線程" 。每當遇到異步任務,交給 EventLoop 線程,然后自己往后運行,等到主線程運行完后,再去 EventLoop 線程拿結果。

          1)所有任務都在主線程上執行,形成一個執行棧(execution context stack)。

          2)主線程之外,還存在一個"任務隊列"(task queue)。系統把異步任務放到"任務隊列"之中,然后繼續執行后續的任務。

          3)一旦"執行棧"中的所有任務執行完畢,系統就會讀取"任務隊列"。如果這個時候,異步任務已經結束了等待狀態,就會從"任務隊列"進入執行棧,恢復執行。

          4)主線程不斷重復上面的第三步。

          "回調函數"(callback),就是那些會被主線程掛起來的代碼。異步任務必須指定回調函數,當異步任務從"任務隊列"回到執行棧,回調函數就會執行。"任務隊列"是一個先進先出的數據結構,排在前面的事件,優先返回主線程。主線程的讀取過程基本上是自動的,只要執行棧一清空,"任務隊列"上第一位的事件就自動返回主線程。

          主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,所以整個的這種運行機制又稱為Event Loop。

          從主線程的角度看,一個異步過程包括下面兩個要素:

          發起函數(或叫注冊函數)A
          回調函數callbackFn
          它們都是在主線程上調用的,其中注冊函數用來發起異步過程,回調函數用來處理結果。

          異步進程有:

          類似onclick等,由瀏覽器內核的DOM binding模塊處理,事件觸發時,回調函數添加到任務隊列中;
          setTimeout等,由瀏覽器內核的Timer模塊處理,時間到達時,回調函數添加到任務隊列中;
          Ajax,由瀏覽器內核的Network模塊處理,網絡請求返回后,添加到任務隊列中。

          例如setTimeout(fn, 1000),其中的setTimeout就是異步過程的發起函數,fn是回調函數。用一句話概括:工作線程將消息放到消息隊列,主線程通過事件循環過程去取消息。

          消息隊列:消息隊列是一個先進先出的隊列,它里面存放著各種消息。
          事件循環:事件循環是指主線程重復從消息隊列中取消息、執行的過程。

          流程如下:

          1) 主線程讀取js代碼, 形成相應的堆和執行棧, 執行同步任務
          2) 當主線程遇到異步任務,,指定給異步進程處理, 同時繼續執行同步任務
          3) 當異步進程處理完畢后, 將相應的異步任務推入到任務隊列首部
          4) 主線程任務處理完畢后,,查詢任務隊列,則取出一個任務隊列推入到主線程的執行棧
          5) 重復執行第2、3、4步,這就稱為事件循環

          JS-Web-API 知識點與高頻考題解析

          BOM

          BOM(瀏覽器對象模型)是瀏覽器本身的一些信息的設置和獲取,例如獲取瀏覽器的寬度、高度,設置讓瀏覽器跳轉到哪個地址。
          navigator: 獲取瀏覽器特性(即俗稱的UA)然后識別客戶端
          location: 獲取網址、協議、path、參數、hash 等
          history: 操作瀏覽器的歷史紀錄,(前進,后退等功能)

          1.什么是window對象? 什么是document對象?

          window:它是一個頂層對象,而不是另一個對象的屬性,即瀏覽器的窗口。
          document:代表整個HTML 文檔,可用來訪問頁面中的所有元素
          Window 對象表示當前瀏覽器的窗口,是JavaScript的頂級對象。我們創建的所有對象、函數、變量都是 Window 對象的成員。
          Window 對象的方法和屬性是在全局范圍內有效的。
          Document 對象是 HTML 文檔的根節點與所有其他節點(元素節點,文本節點,屬性節點, 注釋節點)
          Document 對象使我們可以通過腳本對 HTML 頁面中的所有元素進行訪問
          Document 對象是 Window 對象的一部分,可通過 window.document 屬性對其進行訪問

          2.事件是?IE與火狐的事件機制有什么區別? 如何阻止冒泡?

          1) 我們在網頁中的某個操作(有的操作對應多個事件)。例如:當我們點擊一個按鈕就會產生一個事件。是可以被 JavaScript 偵測到的行為。
          2) 事件處理機制:IE是事件冒泡、Firefox同時支持兩種事件模型,也就是:捕獲型事件和冒泡型事件;
          3) ev.stopPropagation();(舊ie的方法 ev.cancelBubble = true;)

          3.解釋一下事件代理

          事件代理的原理其實就和作用域鏈的原理差不多,但是事件代理是利用事件的冒泡原理來實現的,事件代理就是通過給祖先元素添加事件,通過事件目標對象開始向上查找找到匹配的子節點為止,如果找不到則到綁定事件的那個祖先元素為止,找到了就觸發事件,并且可以通過js中call和apply來改變觸發事件函數中的this為當前綁定節點,也是通過一層一層逐層向上的方式進行匹配查找而觸發對應事件,好處就是可以使后添加的dom元素也同樣有之前存在元素的事件,jquery中可以使用on,delegate,live實現的,不過在jquery1.7版本以后吧live給廢除了,原因就是live綁定事件的祖先元素是整個html頁面的根節點,所以性能消耗比較大,在后邊的版本中給刪除了,使用on,delegate代替

          優點:

          使代碼簡潔
          減少瀏覽器的內存占用

          缺點:

          使用不當會造成事件在不應該觸發時觸發

          function bindEvent(elem, type, selector, fn) { 
              // 這樣處理,可接收兩種調用方式 bindEvent(div1, 'click', 'a', function () {...}) 和 bindEvent(div1, 'click', function () {...}) 這兩種 
              if (fn == null) { 
                  fn = selector 
                  selector = null 
              } 
          
              // 綁定事件 
              elem.addEventListener(type, function (e) { 
                  var target 
                  if (selector) { 
                      // 有 selector 說明需要做事件代理 
                      // 獲取觸發時間的元素,即 e.target 
                      target = e.target 
                      // 看是否符合 selector 這個條件 
                      if (target.matches(selector)) { 
                          fn.call(target, e) 
                      } 
                  } else { 
                      // 無 selector ,說明不需要事件代理 
                      fn(e) 
                  } 
              }) 
          } 
          // 使用代理,bindEvent 多一個 'a' 參數 
          var div1 = document.getElementById('div1') 
          bindEvent(div1, 'click', 'a', function (e) { 
              console.log(this.innerHTML) 
          }) 
          
          // 不使用代理 
          var a = document.getElementById('a1') 
          bindEvent(div1, 'click', function (e) { 
              console.log(a.innerHTML) 
          }) 
          

          4.offsetWidth/offsetHeight,clientWidth/clientHeight與scrollWidth/scrollHeight的區別

          offsetWidth/offsetHeight返回值包含content + padding + border,效果與e.getBoundingClientRect()相同
          clientWidth/clientHeight返回值只包含content + padding,如果有滾動條,也不包含滾動條
          scrollWidth/scrollHeight返回值包含content + padding + 溢出內容的尺寸

          5.focus/blur與focusin/focusout的區別與聯系

          focus/blur不冒泡,focusin/focusout冒泡
          focus/blur兼容性好,focusin/focusout在除FireFox外的瀏覽器下都保持良好兼容性,如需使用事件托管,可考慮在FireFox下使用事件捕獲elem.addEventListener('focus', handler, true)

          可獲得焦點的元素:

          window
          鏈接被點擊或鍵盤操作
          表單空間被點擊或鍵盤操作
          設置tabindex屬性的元素被點擊或鍵盤操作

          6.mouseover/mouseout與mouseenter/mouseleave的區別與聯系

          mouseover/mouseout是標準事件,所有瀏覽器都支持;mouseenter/mouseleave是IE5.5引入的特有事件后來被DOM3標準采納,現代標準瀏覽器也支持
          mouseover/mouseout是冒泡事件;mouseenter/mouseleave不冒泡。需要為多個元素監聽鼠標移入/出事件時,推薦mouseover/mouseout托管,提高性能
          標準事件模型中event.target表示發生移入/出的元素,vent.relatedTarget對應移出/如元素;在老IE中event.srcElement表示發生移入/出的元素,event.toElement表示移出的目標元素,event.fromElement表示移入時的來源元素

          7.介紹DOM0,DOM2,DOM3事件處理方式區別

          DOM0級事件處理方式:


          btn.onclick = func;
          btn.onclick = null;

          DOM2級事件處理方式:


          btn.addEventListener('click', func, false); 
             btn.removeEventListener('click', func, false); 
             btn.attachEvent("onclick", func); 
             btn.detachEvent("onclick", func); 
          

          DOM3級事件處理方式:

          eventUtil.addListener(input, "textInput", func);
          eventUtil 是自定義對象,textInput 是DOM3級事件

          8.事件的三個階段

          捕獲、目標、冒泡

          js的冒泡(Bubbling Event)和捕獲(Capture Event)的區別

          冒泡型事件:事件按照從最特定的事件目標到最不特定的事件目標(document對象)的順序觸發。
          捕獲型事件(event capturing):事件從最不精確的對象(document 對象)開始觸發,然后到最精確(也可以在窗口級別捕獲事件,不過必須由開發人員特別指定)。
          DOM事件流:同時支持兩種事件模型:捕獲型事件和冒泡型事件,但是,捕獲型事件先發生。兩種事件流會觸及DOM中的所有對象,從document對象開始,也在document對象結束。

          事件捕獲

          當你使用事件捕獲時,父級元素先觸發,子級元素后觸發,即div先觸發,p后觸發。

          事件冒泡

          當你使用事件冒泡時,子級元素先觸發,父級元素后觸發,即p先觸發,div后觸發。

          阻止冒泡

          • 在W3c中,使用stopPropagation()方法
          • 在IE下設置cancelBubble = true;

          在捕獲的過程中stopPropagation();后,后面的冒泡過程也不會發生了。

          阻止捕獲

          阻止事件的默認行為,例如click 后的跳轉

          • 在W3c中,使用preventDefault()方法;
          • 在IE下設置window.event.returnValue = false;

          9.介紹事件“捕獲”和“冒泡”執行順序和事件的執行次數?

          按照W3C標準的事件:首是進入捕獲階段,直到達到目標元素,再進入冒泡階段
          事件執行次數(DOM2-addEventListener):元素上綁定事件的個數
          注意1:前提是事件被確實觸發
          注意2:事件綁定幾次就算幾個事件,即使類型和功能完全一樣也不會“覆蓋”
          事件執行順序:判斷的關鍵是否目標元素
          非目標元素:根據W3C的標準執行:捕獲->目標元素->冒泡(不依據事件綁定順序)
          目標元素:依據事件綁定順序:先綁定的事件先執行(不依據捕獲冒泡標準)
          最終順序:父元素捕獲->目標元素事件1->目標元素事件2->子元素捕獲->子元素冒泡->父元素冒泡
          注意:子元素事件執行前提 事件確實“落”到子元素布局區域上,而不是簡單的具有嵌套關系
          在一個DOM上同時綁定兩個點擊事件:一個用捕獲,一個用冒泡。事件會執行幾次,先執行冒泡還是捕獲?

          該DOM上的事件如果被觸發,會執行兩次(執行次數等于綁定次數)
          如果該DOM是目標元素,則按事件綁定順序執行,不區分冒泡/捕獲
          如果該DOM是處于事件流中的非目標元素,則先執行捕獲,后執行冒泡

          10.window.onload 和 document.DOMContentLoaded (注:$(document).ready()) 的區別?

          一般情況下,DOMContentLoaded事件要在window.onload之前執行,當DOM樹構建完成的時候就會執行DOMContentLoaded事件,而window.onload是在頁面載入完成的時候,才執行,這其中包括圖片等元素。大多數時候我們只是想在DOM樹構建完成后,綁定事件到元素,我們并不需要圖片元素,加上有時候加載外域圖片的速度非常緩慢。

          DOM

          講 DOM 先從 HTML 講起,講 HTML 先從 XML 講起。XML 是一種可擴展的標記語言,所謂可擴展就是它可以描述任何結構化的數據,它是一棵樹!

          1.documen.write和 innerHTML的區別

          document.write只能重繪整個頁面
          innerHTML可以重繪頁面的一部分

          2.DOM操作——怎樣添加、移除、移動、復制、創建和查找節點?

          1)創建新節點

          createDocumentFragment()    //創建一個DOM片段 
          createElement()   //創建一個具體的元素 
          createTextNode()   //創建一個文本節點 
          

          2)添加、移除、替換、插入

          appendChild() 
          removeChild() 
          replaceChild() 
          insertBefore() //在已有的子節點前插入一個新的子節點 
          

          3)查找

          getElementsByTagName()    //通過標簽名稱 
          getElementsByName()    //通過元素的Name屬性的值(IE容錯能力較強,會得到一個數組,其中包括id等于name值的) 
          getElementById()    //通過元素Id,唯一性
          
          
          

          3.attribute和property的區別是什么?

          attribute是dom元素在文檔中作為html標簽擁有的屬性;
          property就是dom元素在js中作為對象擁有的屬性。
          所以:
          對于html的標準屬性來說,attribute和property是同步的,是會自動更新的,
          但是對于自定義的屬性來說,他們是不同步的,

          4.src和href的區別

          src用于替換當前元素,href用于在當前文檔和引用資源之間確立聯系。
          src是source的縮寫,指向外部資源的位置,指向的內容將會嵌入到文檔中當前標簽所在位置;在請求src資源時會將其指向的資源下載并應用到文檔內,當瀏覽器解析到該元素時,會暫停其他資源的下載和處理,直到將該資源加載、編譯、執行完畢,圖片和框架等元素也如此,類似于將所指向資源嵌入當前標簽內。這也是為什么將js腳本放在底部而不是頭部。
          Src source,指向外部資源的位置,如果我們添加<script src ="js.js"></script>瀏覽器會暫停其他資源的下載和處理,直到該資源加載,編譯,執行完畢(圖片和框架也是如此),這也就是為什么js腳本要放在底部。
          src用于替換當前元素,href用于在當前文檔和引入資源之間建立聯系。

          對象

          1,JavaScript繼承的幾種實現方式?

          1)構造函數繼承,使用call和apply兩個方法的特性可以實現,改變方法中的this 
          2)原型鏈繼承 
          3)組合式繼承 
          

          2.javascript創建對象的幾種方式?

          javascript創建對象簡單的說,無非就是使用內置對象或各種自定義對象,當然還可以用JSON;但寫法有很多種,也能混合使用。
          1) 對象字面量的方式 person={firstname:"Mark",lastname:"Yun",age:25,eyecolor:"black"};
          2) 用function來模擬無參的構造函數

          function Person(){}
          var person=new Person();//定義一個function,如果使用new"實例化",該function可以看作是一個Class
          person.name=“Mark";
          person.age="25";
          person.work=function(){
          alert(person.name+" hello...");
          }
          person.work();

          3) 用function來模擬參構造函數來實現(用this關鍵字定義構造的上下文屬性)

          function Pet(name,age,hobby){
          this.name=name;//this作用域:當前對象
          this.age=age;
          this.hobby=hobby;
          this.eat=function(){
          alert("我叫"+this.name+",我喜歡"+this.hobby+",是個程序員");
          }
          }
          var maidou =new Pet("麥兜",25,"coding");//實例化、創建對象
          maidou.eat();//調用eat方法

          4) 用工廠方式來創建(內置對象)

           var wcDog =new Object(); 
                                wcDog.name="旺財"; 
                                wcDog.age=3; 
                                wcDog.work=function(){ 
                                             alert("我是"+wcDog.name+",汪汪汪......"); 
                                  } 
                     wcDog.work(); 
          5、用原型方式來創建 
                     function Dog(){ } 
                     Dog.prototype.name="旺財"; 
                     Dog.prototype.eat=function(){alert(this.name+"是個吃貨");} 
                     var wangcai =new Dog(); 
                     wangcai.eat(); 
          

          5) 用混合方式來創建

          function Car(name,price){ 
                                 this.name=name; 
                                 this.price=price; 
                     } 
                     Car.prototype.sell=function(){ 
                                 alert("我是"+this.name+",我現在賣"+this.price+"萬元"); 
                      } 
                      var camry =new Car("凱美瑞",27); 
                     camry.sell();
          
          
          

          3.談談This對象的理解。

          this分為幾個不同的使用場景,在function中this指的的是window,如果是實用new 調用的話this指的是當前的實例化對象,在事件調用函數中this指的調用事件的window特殊的是在IE中的attachEvent中的this總是指向全局對象Window;,在定時器中this指的是window,在es6中有一個箭頭函數,在箭頭函數中this永遠指向的是父級對象,this也是可以改變的,在js中call, apply, bind都可以改變this的指向, call, apply都是執行一個函數并且改變this,區別就是參數傳遞不一樣,而bind是返回一個綁定this之后的新函數

          4.javascript 代碼中的"use strict";是什么意思 ? 使用它區別是什么?

          use strict是一種ECMAscript 5 添加的(嚴格)運行模式,這種模式使得 Javascript 在更嚴格的條件下運行,

          使JS編碼更加規范化的模式,消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為。
          默認支持的糟糕特性都會被禁用,比如不能用with,也不能在意外的情況下給全局變量賦值;
          全局變量的顯示聲明,函數必須聲明在頂層,不允許在非函數代碼塊內聲明函數,arguments.callee也不允許使用;
          消除代碼運行的一些不安全之處,保證代碼運行的安全,限制函數中的arguments修改,嚴格模式下的eval函數的行為和非嚴格模式的也不相同;

          提高編譯器效率,增加運行速度;
          為未來新版本的Javascript標準化做鋪墊。

          5.JSON 的了解?

          JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。
          它是基于JavaScript的一個子集。數據格式簡單, 易于讀寫, 占用帶寬小
          如:{"age":"12", "name":"back"}
          JSON字符串轉換為JSON對象:

          var obj =eval('('+ str +')'); 
          var obj = str.parseJSON(); 
          var obj = JSON.parse(str); 
          

          JSON對象轉換為JSON字符串:

          var last=obj.toJSONString(); 
          var last=JSON.stringify(obj);
          

          6. .call() 和 .apply() 的區別?

          7.什么是函數節流?介紹一下應用場景和原理?

          函數節流(throttle)是指阻止一個函數在很短時間間隔內連續調用。 只有當上一次函數執行后達到規定的時間間隔,才能進行下一次調用。 但要保證一個累計最小調用間隔(否則拖拽類的節流都將無連續效果)

          函數節流用于 onresize, onscroll 等短時間內會多次觸發的事件

          函數節流的原理:使用定時器做時間節流。 當觸發一個事件時,先用 setTimout 讓這個事件延遲一小段時間再執行。 如果在這個時間間隔內又觸發了事件,就 clearTimeout 原來的定時器, 再 setTimeout 一個新的定時器重復以上流程。

          函數節流簡單實現:

          function throttle(method, context) { 
              clearTimeout(methor.tId); 
              method.tId = setTimeout(function(){ 
                  method.call(context); 
              }, 100); // 兩次調用至少間隔 100ms 
          } 
          // 調用 
          window.onresize = function(){ 
             throttle(myFunc, window); 
          } 
          

          8.new 操作符具體干了什么?

          創建實例對象,this 變量引用該對象,同時還繼承了構造函數的原型
          屬性和方法被加入到 this 引用的對象中
          新創建的對象由 this 所引用,并且最后隱式的返回 this

          new共經歷了四個過程。

          var fn = function () { }; 
          var fnObj = new fn(); 
          

          1)創建了一個空對象

          var obj = new object(); 
          

          2)設置原型鏈

          obj._proto_ = fn.prototype; 
          

          3)讓fn的this指向obj,并執行fn的函數體

          var result = fn.call(obj); 
          

          4)判斷fn的返回值類型,如果是值類型,返回obj。如果是引用類型,就返回這個引用類型的對象。

          if (typeof(result) == "object"){ 
          fnObj = result; 
          } else { 
          fnObj = obj;} 
          

          兼容與優化

          1.頁面重構怎么操作?

          網站重構:在不改變外部行為的前提下,簡化結構、添加可讀性,而在網站前端保持一致的行為。
          也就是說是在不改變UI的情況下,對網站進行優化,在擴展的同時保持一致的UI。
          對于傳統的網站來說重構通常是:
          表格(table)布局改為DIV+CSS
          使網站前端兼容于現代瀏覽器(針對于不合規范的CSS、如對IE6有效的)
          對于移動平臺的優化
          針對于SEO進行優化
          深層次的網站重構應該考慮的方面
          減少代碼間的耦合
          讓代碼保持彈性
          嚴格按規范編寫代碼
          設計可擴展的API
          代替舊有的框架、語言(如VB)
          增強用戶體驗

          通常來說對于速度的優化也包含在重構中
          壓縮JS、CSS、image等前端資源(通常是由服務器來解決)
          程序的性能優化(如數據讀寫)
          采用CDN來加速資源加載
          對于JS DOM的優化
          HTTP服務器的文件緩存

          2.列舉IE與其他瀏覽器不一樣的特性?

          1)事件不同之處:
          1-1,觸發事件的元素被認為是目標(target)。而在 IE 中,目標包含在 event 對象的 srcElement 屬性;
          1-2,獲取字符代碼、如果按鍵代表一個字符(shift、ctrl、alt除外),IE 的 keyCode 會返回字符代碼(Unicode),DOM 中按鍵的代碼和字符是分離的,要獲取字符代碼,需要使用 charCode 屬性;
          1-3,阻止某個事件的默認行為,IE 中阻止某個事件的默認行為,必須將 returnValue 屬性設置為 false,Mozilla 中,需要調用 preventDefault() 方法;
          1-4,停止事件冒泡,IE 中阻止事件進一步冒泡,需要設置 cancelBubble 為 true,Mozzilla 中,需要調用 stopPropagation();

          3.什么叫優雅降級和漸進增強?

          優雅降級:Web站點在所有新式瀏覽器中都能正常工作,如果用戶使用的是老式瀏覽器,則代碼會針對舊版本的IE進行降級處理了,使之在舊式瀏覽器上以某種形式降級體驗卻不至于完全不能用。
          如:border-shadow

          漸進增強:從被所有瀏覽器支持的基本功能開始,逐步地添加那些只有新版本瀏覽器才支持的功能,向頁面增加不影響基礎瀏覽器的額外樣式和功能的。當瀏覽器支持時,它們會自動地呈現出來并發揮作用。
          如:默認使用flash上傳,但如果瀏覽器支持 HTML5 的文件上傳功能,則使用HTML5實現更好的體驗;

          4.說說嚴格模式的限制

          嚴格模式主要有以下限制:
          變量必須聲明后再使用
          函數的參數不能有同名屬性,否則報錯
          不能使用with語句
          不能對只讀屬性賦值,否則報錯
          不能使用前綴0表示八進制數,否則報錯
          不能刪除不可刪除的屬性,否則報錯
          不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop]
          eval不會在它的外層作用域引入變量
          eval和arguments不能被重新賦值
          arguments不會自動反映函數參數的變化
          不能使用arguments.callee
          不能使用arguments.caller
          禁止this指向全局對象
          不能使用fn.caller和fn.arguments獲取函數調用的堆棧
          增加了保留字(比如protected、static和interface)
          設立"嚴格模式"的目的,主要有以下幾個:
          消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
          消除代碼運行的一些不安全之處,保證代碼運行的安全;
          提高編譯器效率,增加運行速度;
          為未來新版本的Javascript做好鋪墊。
          注:經過測試IE6,7,8,9均不支持嚴格模式。

          5.檢測瀏覽器版本版本有哪些方式?

          根據 navigator.userAgent // UA.toLowerCase().indexOf('chrome')
          根據 window 對象的成員 // 'ActiveXObject' in window

          6.總結前端性能優化的解決方案

          優化原則和方向
          性能優化的原則是以更好的用戶體驗為標準,具體就是實現下面的目標:
          多使用內存、緩存或者其他方法
          減少 CPU 和GPU 計算,更快展現

          優化的方向有兩個:
          減少頁面體積,提升網絡加載
          優化頁面渲染

          減少頁面體積,提升網絡加載
          靜態資源的壓縮合并(JS 代碼壓縮合并、CSS 代碼壓縮合并、雪碧圖)
          靜態資源緩存(資源名稱加 MD5 戳)
          使用 CDN 讓資源加載更快

          優化頁面渲染
          CSS 放前面,JS 放后面
          懶加載(圖片懶加載、下拉加載更多)
          減少DOM 查詢,對 DOM 查詢做緩存
          減少DOM 操作,多個操作盡量合并在一起執行(DocumentFragment)
          事件節流
          盡早執行操作(DOMContentLoaded)
          使用 SSR 后端渲染,數據直接輸出到 HTML 中,減少瀏覽器使用 JS 模板渲染頁面 HTML 的時間

          7.圖片懶加載與預加載

          圖片懶加載的原理就是暫時不設置圖片的src屬性,而是將圖片的url隱藏起來,比如先寫在data-src里面,等某些事件觸發的時候(比如滾動到底部,點擊加載圖片)再將圖片真實的url放進src屬性里面,從而實現圖片的延遲加載
          圖片預加載是指在一些需要展示大量圖片的網站,實現圖片的提前加載。從而提升用戶體驗。常用的方式有兩種,一種是隱藏在css的background的url屬性里面,一種是通過javascript的Image對象設置實例對象的src屬性實現圖片的預加載。相關代碼如下:

          CSS預加載圖片方式:

          #preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }   
          #preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }   
          #preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; } 
          

          Javascript預加載圖片的方式:

          function preloadImg(url) { 
             var img = new Image(); 
             img.src = url; 
             if(img.complete) { 
                 //接下來可以使用圖片了 
                 //do something here 
             } else { 
                 img.onload = function() { 
                     //接下來可以使用圖片了 
                     //do something here 
                 }; 
             } 
          } 
          

          5.描述瀏覽器的渲染過程,DOM樹和渲染樹的區別?

          瀏覽器的渲染過程:
          解析HTML構建 DOM(DOM樹),并行請求 css/image/js
          CSS 文件下載完成,開始構建 CSSOM(CSS樹)
          CSSOM 構建結束后,和 DOM 一起生成 Render Tree(渲染樹)
          布局(Layout):計算出每個節點在屏幕中的位置
          顯示(Painting):通過顯卡把頁面畫到屏幕上
          DOM樹 和 渲染樹 的區別:
          DOM樹與HTML標簽一一對應,包括head和隱藏元素
          渲染樹不包括head和隱藏元素,大段文本的每一個行都是獨立節點,每一個節點都有對應的css屬性

          7.重繪和回流(重排)的區別和關系?

          重繪:當渲染樹中的元素外觀(如:顏色)發生改變,不影響布局時,產生重繪
          回流:當渲染樹中的元素的布局(如:尺寸、位置、隱藏/狀態狀態)發生改變時,產生重繪回流
          注意:JS獲取Layout屬性值(如:offsetLeft、scrollTop、getComputedStyle等)也會引起回流。因為瀏覽器需要通過回流計算最新值
          回流必將引起重繪,而重繪不一定會引起回流

          8.如何最小化重繪(repaint)和回流(reflow)?

          需要要對元素進行復雜的操作時,可以先隱藏(display:"none"),操作完成后再顯示

          需要創建多個DOM節點時,使用DocumentFragment創建完后一次性的加入document
          緩存Layout屬性值,
          如:var left = elem.offsetLeft; 這樣,多次使用 left 只產生一次回流

          盡量避免用table布局(table元素一旦觸發回流就會導致table里所有的其它元素回流)

          避免使用css表達式(expression),因為每次調用都會重新計算值(包括加載頁面)

          盡量使用 css 屬性簡寫,如:用 border 代替 border-width, border-style, border-color

          批量修改元素樣式:elem.className 和 elem.style.cssText 代替 elem.style.xxx

          9.script 的位置是否會影響首屏顯示時間?

          在解析 HTML 生成 DOM 過程中,js 文件的下載是并行的,不需要 DOM 處理到 script 節點。因此,script的位置不影響首屏顯示的開始時間。
          瀏覽器解析 HTML 是自上而下的線性過程,script作為 HTML 的一部分同樣遵循這個原則
          因此,script 會延遲 DomContentLoad,只顯示其上部分首屏內容,從而影響首屏顯示的完成時間

          存儲

          cookie

          cookie 本身不是用來做服務器端存儲的(計算機領域有很多這種“狗拿耗子”的例子,例如 CSS 中的 float),它是設計用來在服務器和客戶端進行信息傳遞的,因此我們的每個 HTTP 請求都帶著 cookie。但是 cookie 也具備瀏覽器端存儲的能力(例如記住用戶名和密碼),因此就被開發者用上了。

          使用起來也非常簡單,document.cookie = ....即可。

          但是 cookie 有它致命的缺點:

          存儲量太小,只有 4KB
          所有 HTTP 請求都帶著,會影響獲取資源的效率
          API 簡單,需要封裝才能用

          locationStorage 和 sessionStorage

          后來,HTML5 標準就帶來了sessionStorage和localStorage,先拿localStorage來說,它是專門為了瀏覽器端緩存而設計的。

          其優點有:

          存儲量增大到 5MB
          不會帶到 HTTP 請求中
          API 適用于數據存儲 localStorage.setItem(key, value) localStorage.getItem(key)
          sessionStorage的區別就在于它是根據 session 過去時間而實現,而localStorage會永久有效,應用場景不同。例如,一些需要及時失效的重要信息放在sessionStorage中,一些不重要但是不經常設置的信息,放在localStorage中。

          es6/7

          1.說說對es6的理解(說一下es6,知道es6嗎)

          語法糖(箭頭函數,類的定義,繼承),以及一些新的擴展(數組,字符串,對象,方法等),對作用域的重新定義,以及異步編程的解決方案(promise,async,await)、解構賦值的出現

          2.ES6常用特性

          變量定義(let和const,可變與不可變,const定義對象的特殊情況)
          解構賦值
          模板字符串
          數組新API(例:Array.from(),entries(),values(),keys())
          箭頭函數(rest參數,擴展運算符,::綁定this)
          Set和Map數據結構(set實例成員值唯一存儲key值,map實例存儲鍵值對(key-value))
          Promise對象(前端異步解決方案進化史,generator函數,async函數)
          Class語法糖(super關鍵字)

          3.說說你對Promise的理解

          Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件監聽——更合理和更強大。Promise 有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。但是無法獲取到pending狀態,在promise中接受兩個內置參數分別是resolve(成功)和reject(失敗),Promise實例生成以后,可以用then方法分別指定resolved狀態和rejected狀態的回調函數。then方法可以傳遞兩個回調函數第一個是成功,第二個是失敗,失敗回調也可以使用promise的catch方法回調,promise還有一個強大的功能那就是all方法可以組合多個promise實例,包裝成一個新的 Promise 實例。

          4.介紹一下async和await;

          async 會將其后的函數(函數表達式或 Lambda)的返回值封裝成一個 Promise 對象,而 await 會等待這個 Promise 完成,并將其 resolve 的結果返回出來。

          async / await是ES7的重要特性之一,也是目前社區里公認的優秀異步解決方案。目前async / await 在 IE edge中已經可以直接使用了,但是chrome和Node.js還沒有支持。幸運的是,babel已經支持async的transform了,所以我們使用的時候引入babel就行。在開始之前我們需要引入以下的package,preset-stage-3里就有我們需要的async/await的編譯文件。

          5.es6中的Module

          ES6 中模塊化語法更加簡潔,使用export拋出,使用import from 接收,
          如果只是輸出一個唯一的對象,使用export default即可
          // 創建 util1.js 文件,內容如

          export default { 
              a: 100 
          } 
          

          // 創建 index.js 文件,內容如

          import obj from './util1.js' 
          

          如果想要輸出許多個對象,就不能用default了,且import時候要加{...},代碼如下
          // 創建 util2.js 文件,內容如

          export function fn1() { 
              alert('fn1') 
          } 
          export function fn2() { 
              alert('fn2') 
          } 
          

          // 創建 index.js 文件,內容如

          import { fn1, fn2 } from './util2.js’ 
          

          6.ES6 class 和普通構造函數的區別

          class 其實一直是 JS 的關鍵字(保留字),但是一直沒有正式使用,直到 ES6 。 ES6 的 class 就是取代之前構造函數初始化對象的形式,從語法上更加符合面向對象的寫法
          1)class 是一種新的語法形式,是class Name {...}這種形式,和函數的寫法完全不一樣
          2)兩者對比,構造函數函數體的內容要放在 class 中的constructor函數中,constructor即構造器,初始化實例時默認執行
          3)class 中函數的寫法是add() {...}這種形式,并沒有function關鍵字
          而且使用 class 來實現繼承就更加簡單了
          在class中直接extends關鍵字就可以實現繼承,而不像之前的繼承實現有多種不同的實現方式,在es6中就只有一種

          注意以下兩點:
          使用extends即可實現繼承,更加符合經典面向對象語言的寫法,如 Java
          子類的constructor一定要執行super(),以調用父類的constructor

          7.ES6 中新增的數據類型有哪些?

          Set 和 Map 都是 ES6 中新增的數據結構,是對當前 JS 數組和對象這兩種重要數據結構的擴展。由于是新增的數據結構
          1)Set 類似于數組,但數組可以允許元素重復,Set 不允許元素重復
          2)Map 類似于對象,但普通對象的 key 必須是字符串或者數字,而 Map 的 key 可以是任何數據類型

          8.箭頭函數的作用域上下文和 普通函數作用域上下文 的區別

          箭頭函數其實只是一個密名函數的語法糖,區別在于普通函數作用域中的this有特定的指向,一般指向window,而箭頭函數中的this只有一個指向那就是指當前函數所在的對象,其實現原理其實就是類似于之前編程的時候在函數外圍定義that一樣,用了箭頭函數就不用定義that了直接使用this

          9.es6如何轉為es5?

          使用Babel 轉碼器,Babel 的配置文件是.babelrc,存放在項目的根目錄下。使用 Babel 的第一步,就是配置這個文件。

          算法

          1.淺拷貝vs深拷貝

          拷貝其實就是對象復制,為了解決對象復制是產生的引用類型問題
          淺拷貝:利用迭代器,循環對象將對象中的所有可枚舉屬性復制到另一個對象上,但是淺拷貝的有一個問題就是只是拷貝了對象的一級,其他級還如果是引用類型的值的話依舊解決不了
          深拷貝:深拷貝解決了淺拷貝的問題,利用遞歸的形勢便利對象的每一級,實現起來較為復雜,得判斷值是數組還是對象,簡單的說就是,在內存中存在兩個數據結構完全相同又相互獨立的數據,將引用型類型進行復制,而不是只復制其引用關系。

          2.常見的幾種數組排序算法JS實現

          1)快速排序

          從給定的數據中,隨機抽出一項,這項的左邊放所有比它小的,右邊放比它大的,然后再分別這兩邊執行上述操作,采用的是遞歸的思想,總結出來就是 實現一層,分別給兩邊遞歸,設置好出口

          function fastSort(array,head,tail){ 
             //考慮到給每個分區操作的時候都是在原有的數組中進行操作的,所以這里head,tail來確定分片的位置 
             /*生成隨機項*/ 
             var randomnum = Math.floor(ranDom(head,tail)); 
             var random = array[randomnum]; 
             /*將小于random的項放置在其左邊  策略就是通過一個臨時的數組來儲存分好區的結果,再到原數組中替換*/ 
             var arrayTemp = []; 
             var unshiftHead = 0; 
             for(var i = head;i <= tail;i++){ 
               if(array[i]<random){ 
                 arrayTemp.unshift(array[i]); 
                 unshiftHead++; 
               }else if(array[i]>random){ 
                 arrayTemp.push(array[i]); 
               } 
               /*當它等于的時候放哪,這里我想選擇放到隊列的前面,也就是從unshift后的第一個位置放置*/ 
               if(array[i]===random){ 
                 arrayTemp.splice(unshiftHead,0,array[i]); 
               } 
             } 
             /*將對應項覆蓋原來的記錄*/ 
             for(var j = head , u=0;j <= tail;j++,u++){ 
               array.splice(j,1,arrayTemp[u]); 
             } 
             /*尋找中間項所在的index*/ 
             var nowIndex = array.indexOf(random); 
          
             /*設置出口,當要放進去的片段只有2項的時候就可以收工了*/ 
             if(arrayTemp.length <= 2){ 
               return; 
             } 
             /*遞歸,同時應用其左右兩個區域*/ 
             fastSort(array,head,nowIndex); 
             fastSort(array,nowIndex+1,tail); 
          } 
          

          2)插入排序

          思想就是在已經排好序的數組中插入到相應的位置,以從小到大排序為例,掃描已經排好序的片段的每一項,如大于,則繼續往后,直到他小于一項時,將其插入到這項的前面

          function insertSort(array){ 
             /*start根據已排列好的項數決定*/ 
             var start=1; 
             /*按順序,每一項檢查已排列好的序列*/ 
             for(var i=start; i<array.length; start++,i++){ 
               /*跟已排好序的序列做對比,并插入到合適的位置*/ 
               for(var j=0; j<start; j++){ 
                 /*小于或者等于時(我們是升序)插入到該項前面*/ 
                 if(array[i]<=array[j]){ 
                   console.log(array[i]+' '+array[j]); 
                   array.splice(j,0,array[i]); 
                   /*刪除原有項*/ 
                   array.splice(i+1,1); 
                   break; 
                 } 
               } 
          
             } 
          } 
          

          3)冒泡排序

          故名思意 ,就是一個個冒泡到最前端或者最后端,主要是通過兩兩依次比較,以升序為例,如果前一項比后一項大則交換順序,一直比到最后一對

          function bubbleSort(array){ 
             /*給每個未確定的位置做循環*/ 
             for(var unfix=array.length-1; unfix>0; unfix--){ 
               /*給進度做個記錄,比到未確定位置*/ 
               for(var i=0; i<unfix;i++){ 
                 if(array[i]>array[i+1]){ 
                   var temp = array[i]; 
                   array.splice(i,1,array[i+1]); 
                   array.splice(i+1,1,temp); 
                 } 
               } 
             } 
           } 
          

          4)選擇排序

          將當前未確定塊的min或者max取出來插到最前面或者后面

           function selectSort(array){ 
                 /*給每個插入后的未確定的范圍循環,初始是從0開始*/ 
                 for(var unfixed=0; unfixed<array.length; unfixed++){ 
                   /*設置當前范圍的最小值和其索引*/ 
                   var min = array[unfixed]; 
                   var minIndex = unfixed; 
                   /*在該范圍內選出最小值*/ 
                   for(var j=unfixed+1; j<array.length; j++){ 
                     if(min>array[j]){ 
                       min = array[j]; 
                       minIndex = j; 
                     } 
                   } 
                   /*將最小值插入到unfixed,并且把它所在的原有項替換成*/ 
                   array.splice(unfixed,0,min); 
                   array.splice(minIndex+1,1); 
                 } 
               } 
          

          3.寫一個數組去重的方法

          /** 方法一: 
          * 1.構建一個新的數組存放結果 
          * 2.for循環中每次從原數組中取出一個元素,用這個元素循環與結果數組對比 
          * 3.若結果數組中沒有該元素,則存到結果數組中 
          * 缺陷:不能去重數組中得引用類型的值和NaN 
          */ 
          function unique(array){ 
            var result = []; 
            for(var i = 0;i < array.length; i++){ 
              if(result.indexOf(array[i]) == -1) { 
                result.push(array[i]); 
              } 
            } 
            return result; 
          } 
          
          // [1,2,1,2,'1','2',0,'1','你好','1','你好',NaN,NaN] => [1, 2, "1", "2", 0, "你好",NaN,NaN] 
          // [{id: '1'}, {id: '1'}] => [{id: '1'}, {id: '1’}] 
          
          //方法二:ES6 
          Array.from(new Set(array)) 
          // [1,2,1,2,'1','2',0,'1','你好','1','你好',NaN,NaN] => [1, 2, "1", "2", 0, "你好", NaN] 
          

          4.說一下js模板引擎

          模板引擎原理總結起來就是:先獲取html中對應的id下得innerHTML,利用開始標簽和關閉標簽進行字符串切分,其實是將模板劃分成兩部份內容,一部分是html部分,一部分是邏輯部分,通過區別一些特殊符號比如each、if等來將字符串拼接成函數式的字符串,將兩部分各自經過處理后,再次拼接到一起,最后將拼接好的字符串采用new Function()的方式轉化成所需要的函數。
          常用的模版引擎主要有,Template.js,handlebars.js

          5.是否了解公鑰加密和私鑰加密。

          一般情況下是指私鑰用于對數據進行簽名,公鑰用于對簽名進行驗證;
          HTTP網站在瀏覽器端用公鑰加密敏感數據,然后在服務器端再用私鑰解密。

          6.js深度復制的方式

          1)使用jq的$.extend(true, target, obj)
          2)newobj = Object.create(sourceObj),// 但是這個是有個問題就是 newobj的更改不會影響到 sourceobj但是 sourceobj的更改會影響到newObj
          3)newobj = JSON.parse(JSON.stringify(sourceObj))

          7.js設計模式

          總體來說設計模式分為三大類
          創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
          結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
          行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模
          詳細:詳情
          http://www.alloyteam.com/2012...

          8.圖片懶加載與預加載?

          1)圖片懶加載的原理就是暫時不設置圖片的src屬性,而是將圖片的url隱藏起來,比如先寫在data-src里面,等某些事件觸發的時候(比如滾動到底部,點擊加載圖片)再將圖片真實的url放進src屬性里面,從而實現圖片的延遲加載
          Javascript預加載圖片的方式:

          function preloadImg(url) {
              var img = new Image();
              img.src = url;
              if(img.complete) {
                  //接下來可以使用圖片了
                  //do something here
              } else {
                  img.onload = function() {
                      //接下來可以使用圖片了
                      //do something here
                  };
              }
          }
          

          2)圖片預加載,是指在一些需要展示大量圖片的網站,實現圖片的提前加載。從而提升用戶體驗。常用的方式有兩種,一種是隱藏在css的background的url屬性里面,一種是通過javascript的Image對象設置實例對象的src屬性實現圖片的預加載。
          CSS預加載圖片方式:

          #preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }  
          #preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }  
          #preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }
          

          9.統計字符串中次數最多字母

          function findMaxDuplicateChar(str) { 
            if(str.length == 1) { 
              return str; 
            } 
            var charObj = {}; 
            for(var i = 0; i < str.length; i++) { 
              if(!charObj[str.charAt(i)]) { 
                charObj[str.charAt(i)] = 1; 
              } else { 
                charObj[str.charAt(i)] += 1; 
              } 
            } 
            var maxChar = '', 
                maxValue = 1; 
            for(var k in charObj) { 
              if(charObj[k] >= maxValue) { 
                maxChar = k; 
                maxValue = charObj[k]; 
              } 
            } 
            return maxChar + ':' + maxValue; 
          } 
          

          19.變態題目解析

          https://juejin.im/entry/58ada...

          11.對Node的優點和缺點提出了自己的看法?

          • (優點)因為Node是基于事件驅動和無阻塞的,所以非常適合處理并發請求,因此構建在Node上的代理服務器相比其他技術實現(如Ruby)的服務器表現要好得多。此外,與Node代理服務器交互的客戶端代碼是由javascript語言編寫的,因此客戶端和服務器端都用同一種語言編寫,這是非常美妙的事情。
          • (缺點)Node是一個相對新的開源項目,所以不太穩定,它總是一直在變,而且缺少足夠多的第三方庫支持。看起來,就像是Ruby/Rails當年的樣子。

          模塊化

          1.commonjs?requirejs?AMD|CMD|UMD?

          1)CommonJS就是為JS的表現來制定規范,NodeJS是這種規范的實現,webpack 也是以CommonJS的形式來書寫。因為js沒有模塊的功能,所以CommonJS應運而生。但它不能在瀏覽器中運行。 CommonJS定義的模塊分為:{模塊引用(require)} {模塊定義(exports)} {模塊標識(module)}

          2)RequireJS 是一個JavaScript模塊加載器。 RequireJS有兩個主要方法(method): define()和require()。這兩個方法基本上擁有相同的定義(declaration) 并且它們都知道如何加載的依賴關系,然后執行一個回調函數(callback function)。與require()不同的是, define()用來存儲代碼作為一個已命名的模塊。 因此define()的回調函數需要有一個返回值作為這個模塊定義。這些類似被定義的模塊叫作AMD (Asynchronous Module Definition,異步模塊定義)。

          3)AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出 AMD異步加載模塊。它的模塊支持對象 函數 構造器 字符串 JSON等各種類型的模塊。 適用AMD規范適用define方法定義模塊。

          4)CMD是SeaJS 在推廣過程中對模塊定義的規范化產出
          AMD與CDM的區別:
          (1)對于于依賴的模塊,AMD 是提前執行(好像現在也可以延遲執行了),CMD 是延遲執行。
          (2)AMD 推崇依賴前置,CMD 推崇依賴就近。
          (3)AMD 推崇復用接口,CMD 推崇單用接口。
          (4)書寫規范的差異。
          5)umd是AMD和CommonJS的糅合。
          AMD 瀏覽器第一的原則發展 異步加載模塊。
          CommonJS模塊以服務器第一原則發展,選擇同步加載,它的模塊無需包裝(unwrapped modules)。這迫使人們又想出另一個更通用的模式UMD ( Universal Module Definition ), 希望解決跨平臺的解決方案。UMD先判斷是否支持Node.js的模塊( exports )是否存在,存在則使用Node.js模塊模式。

          2.模塊化的理解

          模塊化的話其實主要就是對于js功能邏輯的劃分,在js中我們一般都吧一個js文件定義成一個模塊,模塊主要的職責就是(封裝實現,暴露接口,聲明依賴)

          3.AMD和CMD的區別

          AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出。
          CMD 是 SeaJS 在推廣過程中對模塊定義的規范化產出。

          對于依賴的模塊,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。CMD 推崇 as lazy as possible.
          CMD 推崇依賴就近,AMD 推崇依賴前置。
          AMD 的 API 默認是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,沒有全局 require,而是根據模塊系統的完備性,提供 seajs.use 來實現模塊系統的加載啟動。CMD 里,每個 API 都簡單純粹。

          前端安全

          1.XSS(Cross Site Scripting,跨站腳本攻擊)

          這是前端最常見的攻擊方式,很多大型網站(如 Facebook)都被 XSS 攻擊過。

          舉一個例子,我在一個博客網站正常發表一篇文章,輸入漢字、英文和圖片,完全沒有問題。但是如果我寫的是惡意的 JS 腳本,例如獲取到document.cookie然后傳輸到自己的服務器上,那我這篇博客的每一次瀏覽都會執行這個腳本,都會把訪客 cookie 中的信息偷偷傳遞到我的服務器上來。

          其實原理上就是黑客通過某種方式(發布文章、發布評論等)將一段特定的 JS 代碼隱蔽地輸入進去。然后別人再看這篇文章或者評論時,之前注入的這段 JS 代碼就執行了。JS 代碼一旦執行,那可就不受控制了,因為它跟網頁原有的 JS 有同樣的權限,例如可以獲取 server 端數據、可以獲取 cookie 等。于是,攻擊就這樣發生了。

          XSS的危害

          XSS 的危害相當大,如果頁面可以隨意執行別人不安全的 JS 代碼,輕則會讓頁面錯亂、功能缺失,重則會造成用戶的信息泄露。

          比如早些年社交網站經常爆出 XSS 蠕蟲,通過發布的文章內插入 JS,用戶訪問了感染不安全 JS 注入的文章,會自動重新發布新的文章,這樣的文章會通過推薦系統進入到每個用戶的文章列表面前,很快就會造成大規模的感染。

          還有利用獲取 cookie 的方式,將 cookie 傳入入侵者的服務器上,入侵者就可以模擬 cookie 登錄網站,對用戶的信息進行篡改。

          XSS的預防

          那么如何預防 XSS 攻擊呢?—— 最根本的方式,就是對用戶輸入的內容進行驗證和替換,需要替換的字符有:

          & 替換為:&
          < 替換為:<

          替換為:>
          ” 替換為:"
          ‘ 替換為:'
          / 替換為:/
          替換了這些字符之后,黑客輸入的攻擊代碼就會失效,XSS 攻擊將不會輕易發生。

          除此之外,還可以通過對 cookie 進行較強的控制,比如對敏感的 cookie 增加http-only限制,讓 JS 獲取不到 cookie 的內容。

          2.CSRF(Cross-site request forgery,跨站請求偽造)

          CSRF 是借用了當前操作者的權限來偷偷地完成某個操作,而不是拿到用戶的信息。
          例如,一個支付類網站,給他人轉賬的接口是http://buy.com/pay?touid=999&...,而這個接口在使用時沒有任何密碼或者 token 的驗證,只要打開訪問就直接給他人轉賬。一個用戶已經登錄了http://buy.com,在選擇商品時,突然收到一封郵件,而這封郵件正文有這么一行代碼<img src="http://buy.com/pay?touid=999&money=100"/>,他訪問了郵件之后,其實就已經完成了購買。
          CSRF 的發生其實是借助了一個 cookie 的特性。我們知道,登錄了http://buy.com之后,cookie 就會有登錄過的標記了,此時請求http://buy.com/pay?touid=999&... cookie 的,因此 server 端就知道已經登錄了。而如果在http://buy.com去請求其他域名的 API 例如http://abc.com/api時,是不會帶 cookie 的,這是瀏覽器的同源策略的限制。但是 —— 此時在其他域名的頁面中,請求http://buy.com/pay?touid=999&...,會帶著buy.com的 cookie ,這是發生 CSRF 攻擊的理論基礎。

          預防 CSRF 就是加入各個層級的權限驗證,例如現在的購物網站,只要涉及現金交易,肯定要輸入密碼或者指紋才行。除此之外,敏感的接口使用POST請求而不是GET也是很重要的。


          主站蜘蛛池模板: 亚洲AV色香蕉一区二区| 精品国产一区二区三区麻豆| 老熟女高潮一区二区三区| 后入内射国产一区二区| 亚洲AV无码一区二区三区系列| 海角国精产品一区一区三区糖心| 日本一区二区不卡视频| 精品视频在线观看一区二区| 精品国产一区二区三区麻豆| 亚洲综合色自拍一区| 一区二区国产在线观看| 国产av一区最新精品| 精品欧洲av无码一区二区| 冲田杏梨高清无一区二区| 成人精品视频一区二区三区尤物| 一区二区三区日韩| 久久精品亚洲一区二区| 精品一区二区三区高清免费观看| 国产未成女一区二区三区| 精品欧洲av无码一区二区三区| 久久精品一区二区三区日韩| 久久影院亚洲一区| 久久精品国产一区二区电影| 高清一区二区三区视频| 国产嫖妓一区二区三区无码| 成人区人妻精品一区二区三区| 国产精品一级香蕉一区| 人妻无码一区二区三区AV| 污污内射在线观看一区二区少妇 | 一区二区视频在线免费观看| 日韩一区二区a片免费观看| 亚洲综合无码一区二区痴汉| 亚洲一区二区三区在线观看网站| 色窝窝无码一区二区三区| 久久国产午夜一区二区福利| 人妻久久久一区二区三区 | 日韩毛片一区视频免费| 国产一区二区在线观看视频| 高清一区二区三区视频| 亚洲成人一区二区| 国产av成人一区二区三区|