摘要:變量聲明提升在中,函數聲明與變量聲明經常被引擎隱式地提升到當前作用域的頂部。對象的方法和屬性是在全局范圍內有效的。未形成標準,實現混亂。
前端面試題JavaScript(一)
JavaScript的組成
JavaScript 由以下三部分組成:
ECMAScript(核心):JavaScript 語言基礎
DOM(文檔對象模型):規定了訪問HTML和XML的接口
BOM(瀏覽器對象模型):提供了瀏覽器窗口之間進行交互的對象和方法
JS的基本數據類型和引用數據類型
基本數據類型:undefined、null、boolean、number、string、symbol
引用數據類型:object、array、function
檢測瀏覽器版本版本有哪些方式?
根據 navigator.userAgent // UA.toLowerCase().indexOf("chrome")
根據 window 對象的成員 // "ActiveXObject" in window
介紹JS有哪些內置對象?
數據封裝類對象:Object、Array、Boolean、Number、String
其他對象:Function、Arguments、Math、Date、RegExp、Error
ES6新增對象:Symbol、Map、Set、Promises、Proxy、Reflect
說幾條寫JavaScript的基本規范?
代碼縮進,建議使用“四個空格”縮進
代碼段使用花括號{}包裹
語句結束使用分號;
變量和函數在使用前進行聲明
以大寫字母開頭命名構造函數,全大寫命名常量
規范定義JSON對象,補全雙引號
用{}和[]聲明對象和數組
如何編寫高性能的JavaScript?
遵循嚴格模式:"use strict";
將js腳本放在頁面底部,加快渲染頁面
將js腳本將腳本成組打包,減少請求
使用非阻塞方式下載js腳本
盡量使用局部變量來保存全局變量
盡量減少使用閉包
使用 window 對象屬性方法時,省略 window
盡量減少對象成員嵌套
緩存 DOM 節點的訪問
通過避免使用 eval() 和 Function() 構造器
給 setTimeout() 和 setInterval() 傳遞函數而不是字符串作為參數
盡量使用直接量創建對象和數組
最小化重繪(repaint)和回流(reflow)
描述瀏覽器的渲染過程,DOM樹和渲染樹的區別?
瀏覽器的渲染過程:
解析HTML構建 DOM(DOM樹),并行請求 css/image/js
CSS 文件下載完成,開始構建 CSSOM(CSS樹)
CSSOM 構建結束后,和 DOM 一起生成 Render Tree(渲染樹)
布局(Layout):計算出每個節點在屏幕中的位置
顯示(Painting):通過顯卡把頁面畫到屏幕上
DOM樹 和 渲染樹 的區別:
DOM樹與HTML標簽一一對應,包括head和隱藏元素
渲染樹不包括head和隱藏元素,大段文本的每一個行都是獨立節點,每一個節點都有對應的css屬性
重繪和回流(重排)的區別和關系?
重繪:當渲染樹中的元素外觀(如:顏色)發生改變,不影響布局時,產生重繪
回流:當渲染樹中的元素的布局(如:尺寸、位置、隱藏/狀態狀態)發生改變時,產生重繪回流
注意:JS獲取Layout屬性值(如:offsetLeft、scrollTop、getComputedStyle等)也會引起回流。因為瀏覽器需要通過回流計算最新值
回流必將引起重繪,而重繪不一定會引起回流
如何最小化重繪(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
script 的位置是否會影響首屏顯示時間?
在解析 HTML 生成 DOM 過程中,js 文件的下載是并行的,不需要 DOM 處理到 script 節點。因此,script的位置不影響首屏顯示的開始時間。
瀏覽器解析 HTML 是自上而下的線性過程,script作為 HTML 的一部分同樣遵循這個原則
因此,script 會延遲 DomContentLoad,只顯示其上部分首屏內容,從而影響首屏顯示的完成時間
解釋JavaScript中的作用域與變量聲明提升?
JavaScript作用域:
在Java、C等語言中,作用域為for語句、if語句或{}內的一塊區域,稱為作用域;
而在 JavaScript 中,作用域為function(){}內的區域,稱為函數作用域。
JavaScript變量聲明提升:
在JavaScript中,函數聲明與變量聲明經常被JavaScript引擎隱式地提升到當前作用域的頂部。
聲明語句中的賦值部分并不會被提升,只有名稱被提升
函數聲明的優先級高于變量,如果變量名跟函數名相同且未賦值,則函數聲明會覆蓋變量聲明
如果函數有多個同名參數,那么最后一個參數(即使沒有定義)會覆蓋前面的同名參數
介紹JavaScript的原型,原型鏈?有什么特點?
原型:
JavaScript的所有對象中都包含了一個 [__proto__] 內部屬性,這個屬性所對應的就是該對象的原型
JavaScript的函數對象,除了原型 [__proto__] 之外,還預置了 prototype 屬性
當函數對象作為構造函數創建實例時,該 prototype 屬性值將被作為實例對象的原型 [__proto__]。
原型鏈:
當一個對象調用的屬性/方法自身不存在時,就會去自己 [__proto__] 關聯的前輩 prototype 對象上去找
如果沒找到,就會去該 prototype 原型 [__proto__] 關聯的前輩 prototype 去找。依次類推,直到找到屬性/方法或 undefined 為止。從而形成了所謂的“原型鏈”
原型特點:
JavaScript對象是通過引用來傳遞的,當修改原型時,與之相關的對象也會繼承這一改變
JavaScript有幾種類型的值
原始數據類型(Undefined,Null,Boolean,Number、String)-- 棧
引用數據類型(對象、數組和函數)-- 堆
兩種類型的區別是:存儲位置不同:
原始數據類型是直接存儲在棧(stack)中的簡單數據段,占據空間小、大小固定,屬于被頻繁使用數據;
引用數據類型存儲在堆(heap)中的對象,占據空間大、大小不固定,如果存儲在棧中,將會影響程序運行的性能;
引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。
當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址后從堆中獲得實體。
JavaScript如何實現一個類,怎么實例化這個類?
構造函數法(this + prototype) -- 用 new 關鍵字 生成實例對象
缺點:用到了 this 和 prototype,編寫復雜,可讀性差
function Mobile(name, price){ this.name = name; this.price = price; } Mobile.prototype.sell = function(){ alert(this.name + ",售價 $" + this.price); } var iPhone7 = new Mobile("iPhone7", 1000); iPhone7.sell();
Object.create 法 -- 用 Object.create() 生成實例對象
缺點:不能實現私有屬性和私有方法,實例對象之間也不能共享數據
var Person = { firstname: "Mark", lastname: "Yun", age: 25, introduce: function(){ alert("I am " + Person.firstname + " " + Person.lastname); } }; var person = Object.create(Person); person.introduce(); // Object.create 要求 IE9+,低版本瀏覽器可以自行部署: if (!Object.create) { Object.create = function (o) { function F() {} F.prototype = o; return new F(); }; }
極簡主義法(消除 this 和 prototype) -- 調用 createNew() 得到實例對象
優點:容易理解,結構清晰優雅,符合傳統的"面向對象編程"的構造
var Cat = { age: 3, // 共享數據 -- 定義在類對象內,createNew() 外 createNew: function () { var cat = {}; // var cat = Animal.createNew(); // 繼承 Animal 類 cat.name = "小咪"; var sound = "喵喵喵"; // 私有屬性--定義在 createNew() 內,輸出對象外 cat.makeSound = function () { alert(sound); // 暴露私有屬性 }; cat.changeAge = function(num){ Cat.age = num; // 修改共享數據 }; return cat; // 輸出對象 } }; var cat = Cat.createNew(); cat.makeSound();
ES6 語法糖 class -- 用 new 關鍵字 生成實例對象
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } var point = new Point(2, 3);
Javascript如何實現繼承?
構造函數綁定:使用 call 或 apply 方法,將父對象的構造函數綁定在子對象上
function Cat(name,color){ Animal.apply(this, arguments); this.name = name; this.color = color; }
實例繼承:將子對象的 prototype 指向父對象的一個實例
Cat.prototype = new Animal(); Cat.prototype.constructor = Cat;
拷貝繼承:如果把父對象的所有屬性和方法,拷貝進子對象
function extend(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } c.uber = p; }
原型繼承:將子對象的 prototype 指向父對象的 prototype
function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; }
ES6 語法糖 extends:class ColorPoint extends Point {}
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 調用父類的constructor(x, y) this.color = color; } toString() { return this.color + " " + super.toString(); // 調用父類的toString() } }
談談this對象的理解
this 總是指向函數的直接調用者
如果有 new 關鍵字,this 指向 new 出來的實例對象
在事件中,this指向觸發這個事件的對象
IE下 attachEvent 中的this總是指向全局對象Window
eval是做什么的?
eval的功能是把對應的字符串解析成JS代碼并運行
應該避免使用eval,不安全,非常耗性能(先解析成js語句,再執行)
由JSON字符串轉換為JSON對象的時候可以用 eval("("+ str +")");
什么是 Window 對象? 什么是 Document 對象?
Window 對象表示當前瀏覽器的窗口,是JavaScript的頂級對象。
我們創建的所有對象、函數、變量都是 Window 對象的成員。
Window 對象的方法和屬性是在全局范圍內有效的。
Document 對象是 HTML 文檔的根節點與所有其他節點(元素節點,文本節點,屬性節點, 注釋節點)
Document 對象使我們可以通過腳本對 HTML 頁面中的所有元素進行訪問
Document 對象是 Window 對象的一部分,可通過 window.document 屬性對其進行訪問
介紹 DOM 的發展
DOM:文檔對象模型(Document Object Model),定義了訪問HTML和XML文檔的標準,與編程語言及平臺無關
DOM0:提供了查詢和操作Web文檔的內容API。未形成標準,實現混亂。如:document.forms["login"]
DOM1:W3C提出標準化的DOM,簡化了對文檔中任意部分的訪問和操作。如:JavaScript中的Document對象
DOM2:原來DOM基礎上擴充了鼠標事件等細分模塊,增加了對CSS的支持。如:getComputedStyle(elem, pseudo)
DOM3:增加了XPath模塊和加載與保存(Load and Save)模塊。如:XPathEvaluator
介紹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級事件
事件的三個階段
捕獲、目標、冒泡
介紹事件“捕獲”和“冒泡”執行順序和事件的執行次數?
按照W3C標準的事件:首是進入捕獲階段,直到達到目標元素,再進入冒泡階段
事件執行次數(DOM2-addEventListener):元素上綁定事件的個數
注意1:前提是事件被確實觸發
注意2:事件綁定幾次就算幾個事件,即使類型和功能完全一樣也不會“覆蓋”
事件執行順序:判斷的關鍵是否目標元素
非目標元素:根據W3C的標準執行:捕獲->目標元素->冒泡(不依據事件綁定順序)
目標元素:依據事件綁定順序:先綁定的事件先執行(不依據捕獲冒泡標準)
最終順序:父元素捕獲->目標元素事件1->目標元素事件2->子元素捕獲->子元素冒泡->父元素冒泡
注意:子元素事件執行前提 事件確實“落”到子元素布局區域上,而不是簡單的具有嵌套關系
在一個DOM上同時綁定兩個點擊事件:一個用捕獲,一個用冒泡。事件會執行幾次,先執行冒泡還是捕獲?
該DOM上的事件如果被觸發,會執行兩次(執行次數等于綁定次數)
如果該DOM是目標元素,則按事件綁定順序執行,不區分冒泡/捕獲
如果該DOM是處于事件流中的非目標元素,則先執行捕獲,后執行冒泡
事件的代理/委托
事件委托是指將事件綁定目標元素的到父元素上,利用冒泡機制觸發該事件
優點:
可以減少事件注冊,節省大量內存占用
可以將事件應用于動態添加的子元素上
缺點:
使用不當會造成事件在不應該觸發時觸發
示例:
ulEl.addEventListener("click", function(e){ var target = event.target || event.srcElement; if(!!target && target.nodeName.toUpperCase() === "LI"){ console.log(target.innerHTML); } }, false);
IE與火狐的事件機制有什么區別? 如何阻止冒泡?
IE只事件冒泡,不支持事件捕獲;火狐同時支持件冒泡和事件捕獲
IE的事件處理和W3C的事件處理有哪些區別?
綁定事件
W3C: targetEl.addEventListener("click", handler, false);
IE: targetEl.attachEvent("onclick", handler);
刪除事件
W3C: targetEl.removeEventListener("click", handler, false);
IE: targetEl.detachEvent(event, handler);
事件對象
W3C: var e = arguments.callee.caller.arguments[0]
IE: window.event
事件目標
W3C: e.target
IE: window.event.srcElement
阻止事件默認行為
W3C: e.preventDefault()
IE: window.event.returnValue = false
阻止事件傳播
W3C: e.stopPropagation()
IE: window.event.cancelBubble = true
W3C事件的 target 與 currentTarget 的區別?
target 只會出現在事件流的目標階段
currentTarget 可能出現在事件流的任何階段
當事件流處在目標階段時,二者的指向相同
當事件流處于捕獲或冒泡階段時:currentTarget 指向當前事件活動的對象(一般為父級)
如何派發事件(dispatchEvent)?(如何進行事件廣播?)
W3C: 使用 dispatchEvent 方法
IE: 使用 fireEvent 方法
var fireEvent = function(element, event){ if (document.createEventObject){ var mockEvent = document.createEventObject(); return element.fireEvent("on" + event, mockEvent) }else{ var mockEvent = document.createEvent("HTMLEvents"); mockEvent.initEvent(event, true, true); return !element.dispatchEvent(mockEvent); } }
什么是函數節流?介紹一下應用場景和原理?
函數節流(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); }
區分什么是“客戶區坐標”、“頁面坐標”、“屏幕坐標”?
客戶區坐標:鼠標指針在可視區中的水平坐標(clientX)和垂直坐標(clientY)
頁面坐標:鼠標指針在頁面布局中的水平坐標(pageX)和垂直坐標(pageY)
屏幕坐標:設備物理屏幕的水平坐標(screenX)和垂直坐標(screenY)
如何獲得一個DOM元素的絕對位置?
elem.offsetLeft:返回元素相對于其定位父級左側的距離
elem.offsetTop:返回元素相對于其定位父級頂部的距離
elem.getBoundingClientRect():返回一個DOMRect對象,包含一組描述邊框的只讀屬性,單位像素
分析 ["1", "2", "3"].map(parseInt) 答案是多少?
答案:[1, NaN, NaN]
parseInt(string, radix) 第2個參數 radix 表示進制。省略 radix 或 radix = 0,則數字將以十進制解析
map 每次為 parseInt 傳3個參數(elem, index, array),其中 index 為數組索引
因此,map 遍歷 ["1", "2", "3"],相應 parseInt 接收參數如下
parseInt("1", 0); // 1 parseInt("2", 1); // NaN parseInt("3", 2); // NaN
所以,parseInt 參數 radix 不合法,導致返回值為 NaN
new 操作符具體干了什么?
創建實例對象,this 變量引用該對象,同時還繼承了構造函數的原型
屬性和方法被加入到 this 引用的對象中
新創建的對象由 this 所引用,并且最后隱式的返回 this
用原生JavaScript的實現過什么功能嗎?
封裝選擇器、調用第三方API、設置和獲取樣式
解釋一下這段代碼的意思嗎?
[].forEach.call($$("*"), function(el){ el.style.outline = "1px solid #" + (~~(Math.random()*(1<<24))).toString(16); })
解釋:獲取頁面所有的元素,遍歷這些元素,為它們添加1像素隨機顏色的輪廓(outline)
$$(sel)// $$函數被許多現代瀏覽器命令行支持,等價于 document.querySelectorAll(sel)
[].forEach.call(NodeLists) // 使用 call 函數將數組遍歷函數 forEach 應到節點元素列表
el.style.outline = "1px solid #333" // 樣式 outline 位于盒模型之外,不影響元素布局位置
(1<<24) // parseInt("ffffff", 16) == 16777215 == 2^24 - 1 // 1<<24 == 2^24 == 16777216
Math.random()*(1<<24) // 表示一個位于 0 到 16777216 之間的隨機浮點數
~~Math.random()*(1<<24) // ~~ 作用相當于 parseInt 取整
(~~(Math.random()*(1<<24))).toString(16) // 轉換為一個十六進制-
JavaScript實現異步編程的方法?
回調函數
事件監聽
發布/訂閱
Promises對象
Async函數[ES7]
web開發中會話跟蹤的方法有哪些
cookie
session
url重寫
隱藏input
ip地址
說幾條寫JavaScript的基本規范?
不要在同一行聲明多個變量
請使用 ===/!==來比較true/false或者數值
使用對象字面量替代new Array這種形式
不要使用全局函數
Switch語句必須帶有default分支
函數不應該有時候有返回值,有時候沒有返回值
If語句必須使用大括號
for-in循環中的變量 應該使用var關鍵字明確限定作用域,從而避免作用域污
Javascript如何實現繼承?
構造繼承
原型繼承
實例繼承
拷貝繼承
原型prototype機制或apply和call方法去實現較簡單,建議使用構造函數與原型混合方式
function Parent(){ this.name = "wang"; } function Child(){ this.age = 28; } Child.prototype = new Parent();//繼承了Parent,通過原型 var demo = new Child(); alert(demo.age); alert(demo.name);//得到被繼承的屬性 }
javascript創建對象的幾種方式?
javascript創建對象簡單的說,無非就是使用內置對象或各種自定義對象,當然還可以用JSON;但寫法有很多種,也能混合使用
對象字面量的方式
person={firstname:"Mark",lastname:"Yun",age:25,eyecolor:"black"};
用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();
用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方法
用工廠方式來創建(內置對象)
var wcDog =new Object(); wcDog.name="旺財"; wcDog.age=3; wcDog.work=function(){ alert("我是"+wcDog.name+",汪汪汪......"); } wcDog.work();
用原型方式來創建
function Dog(){ } Dog.prototype.name="旺財"; Dog.prototype.eat=function(){ alert(this.name+"是個吃貨"); } var wangcai =new Dog(); wangcai.eat();
用混合方式來創建
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();
null,undefined 的區別?
undefined 表示不存在這個值。
undefined :是一個表示"無"的原始值或者說表示"缺少值",就是此處應該有一個值,但是還沒有定義。當嘗試讀取時會返回 undefined
例如變量被聲明了,但沒有賦值時,就等于undefined
null 表示一個對象被定義了,值為“空值”
null : 是一個對象(空對象, 沒有任何屬性和方法)
例如作為函數的參數,表示該函數的參數不是對象;
在驗證null時,一定要使用 === ,因為 == 無法分別 null 和 undefined
寫一個通用的事件偵聽器函數
// event(事件)工具集,來源:github.com/markyun markyun.Event = { // 頁面加載完成后 readyEvent : function(fn) { if (fn==null) { fn=document; } var oldonload = window.onload; if (typeof window.onload != "function") { window.onload = fn; } else { window.onload = function() { oldonload(); fn(); }; } }, // 視能力分別使用dom0||dom2||IE方式 來綁定事件 // 參數: 操作的元素,事件名稱 ,事件處理程序 addEvent : function(element, type, handler) { if (element.addEventListener) { //事件類型、需要執行的函數、是否捕捉 element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, function() { handler.call(element); }); } else { element["on" + type] = handler; } }, // 移除事件 removeEvent : function(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.datachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } }, // 阻止事件 (主要是事件冒泡,因為IE不支持事件捕獲) stopPropagation : function(ev) { if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } }, // 取消事件的默認行為 preventDefault : function(event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } }, // 獲取事件目標 getTarget : function(event) { return event.target || event.srcElement; }, // 獲取event對象的引用,取到事件的所有信息,確保隨時能使用event; getEvent : function(e) { var ev = e || window.event; if (!ev) { var c = this.getEvent.caller; while (c) { ev = c.arguments[0]; if (ev && Event == ev.constructor) { break; } c = c.caller; } } return ev; } };
什么是閉包(closure),為什么要用它?
閉包是指有權訪問另一個函數作用域中變量的函數,創建閉包的最常見的方式就是在一個函數內創建另一個函數,通過另一個函數訪問這個函數的局部變量,利用閉包可以突破作用鏈域
閉包的特性:
函數內再嵌套函數
內部函數可以引用外層的參數和變量
參數和變量不會被垃圾回收機制回收
javascript 代碼中的"use strict";是什么意思 ? 使用它區別是什么?
use strict是一種ECMAscript 5 添加的(嚴格)運行模式,這種模式使得 Javascript 在更嚴格的條件下運行,使JS編碼更加規范化的模式,消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為
如何判斷一個對象是否屬于某個類?
// 使用instanceof (待完善) if(a instanceof Person){ alert("yes"); }
new操作符具體干了什么呢?
創建一個空對象,并且 this 變量引用該對象,同時還繼承了該函數的原型
屬性和方法被加入到 this 引用的對象中
新創建的對象由 this 所引用,并且最后隱式的返回 this
var obj = {}; obj.__proto__ = Base.prototype; Base.call(obj);
js延遲加載的方式有哪些?
defer和async、動態創建DOM方式(用得最多)、按需異步載入js
Ajax 是什么? 如何創建一個Ajax?
ajax的全稱:Asynchronous Javascript And XML
異步傳輸+js+xml
所謂異步,在這里簡單地解釋就是:向服務器發送請求的時候,我們不必等待結果,而是可以同時做其他的事情,等到有了結果它自己會根據設定進行后續操作,與此同時,頁面是不會發生整頁刷新的,提高了用戶體驗
創建XMLHttpRequest對象,也就是創建一個異步調用對象
建一個新的HTTP請求,并指定該HTTP請求的方法、URL及驗證信息
設置響應HTTP請求狀態變化的函數
發送HTTP請求
獲取異步調用返回的數據
用JavaScript和DOM實現局部刷新
同步和異步的區別?
同步:瀏覽器訪問服務器請求,用戶看得到頁面刷新,重新發請求,等請求完,頁面刷新,新內容出現,用戶看到新內容,進行下一步操作
異步:瀏覽器訪問服務器請求,用戶正常操作,瀏覽器后端進行請求。等請求完,頁面不刷新,新內容也會出現,用戶看到新內容
documen.write和 innerHTML的區別
document.write只能重繪整個頁面
innerHTML可以重繪頁面的一部分
DOM操作——怎樣添加、移除、移動、復制、創建和查找節點?
(1)創建新節點
createDocumentFragment() //創建一個DOM片段
createElement() //創建一個具體的元素
createTextNode() //創建一個文本節點
(2)添加、移除、替換、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子節點前插入一個新的子節點
(3)查找
getElementsByTagName() //通過標簽名稱
getElementsByName() // 通過元素的Name屬性的值(IE容錯能力較強,會得到一個數組,其中包括id等于name值的)
getElementById() //通過元素Id,唯一性
那些操作會造成內存泄漏?
內存泄漏指任何對象在您不再擁有或需要它之后仍然存在
垃圾回收器定期掃描對象,并計算引用了每個對象的其他對象的數量。如果一個對象的引用數量為 0(沒有其他對象引用過該對象),或對該對象的惟一引用是循環的,那么該對象的內存即可回收
setTimeout 的第一個參數使用字符串而非函數的話,會引發內存泄漏
閉包、控制臺日志、循環(在兩個對象彼此引用且彼此保留時,就會產生一個循環)
漸進增強和優雅降級
漸進增強 :針對低版本瀏覽器進行構建頁面,保證最基本的功能,然后再針對高級瀏覽器進行效果、交互等改進和追加功能達到更好的用戶體驗。
優雅降級 :一開始就構建完整的功能,然后再針對低版本瀏覽器進行兼容
Javascript垃圾回收方法
標記清除(mark and sweep)
這是JavaScript最常見的垃圾回收方式,當變量進入執行環境的時候,比如函數中聲明一個變量,垃圾回收器將其標記為“進入環境”,當變量離開環境的時候(函數執行結束)將其標記為“離開環境”
垃圾回收器會在運行的時候給存儲在內存中的所有變量加上標記,然后去掉環境中的變量以及被環境中變量所引用的變量(閉包),在這些完成之后仍存在標記的就是要刪除的變量了
引用計數(reference counting)
在低版本IE中經常會出現內存泄露,很多時候就是因為其采用引用計數方式進行垃圾回收。引用計數的策略是跟蹤記錄每個值被使用的次數,當聲明了一個 變量并將一個引用類型賦值給該變量的時候這個值的引用次數就加1,如果該變量的值變成了另外一個,則這個值得引用次數減1,當這個值的引用次數變為0的時 候,說明沒有變量在使用,這個值沒法被訪問了,因此可以將其占用的空間回收,這樣垃圾回收器會在運行的時候清理掉引用次數為0的值占用的空間
js繼承方式及其優缺點
原型鏈繼承的缺點
一是字面量重寫原型會中斷關系,使用引用類型的原型,并且子類型還無法給超類型傳遞參數。
借用構造函數(類式繼承)
借用構造函數雖然解決了剛才兩種問題,但沒有原型,則復用無從談起。所以我們需要原型鏈+借用構造函數的模式,這種模式稱為組合繼承
組合式繼承
組合式繼承是比較常用的一種繼承方法,其背后的思路是使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承。這樣,既通過在原型上定義方法實現了函數復用,又保證每個實例都有它自己的屬性。
defer和async
defer并行加載js文件,會按照頁面上script標簽的順序執行async并行加載js文件,下載完成立即執行,不會按照頁面上script標簽的順序執行
用過哪些設計模式?
工廠模式:
主要好處就是可以消除對象間的耦合,通過使用工程方法而不是new關鍵字。將所有實例化的代碼集中在一個位置防止代碼重復
工廠模式解決了重復實例化的問題 ,但還有一個問題,那就是識別問題,因為根本無法 搞清楚他們到底是哪個對象的實例
-
function createObject(name,age,profession){//集中實例化的函數var obj = new Object(); obj.name = name; obj.age = age; obj.profession = profession; obj.move = function () { return this.name + " at " + this.age + " engaged in " + this.profession; }; return obj; } var test1 = createObject("trigkit4",22,"programmer");//第一個實例var test2 = createObject("mike",25,"engineer");//第二個實例
構造函數模式
使用構造函數的方法 ,即解決了重復實例化的問題 ,又解決了對象識別的問題,該模式與工廠模式的不同之處在于
構造函數方法沒有顯示的創建對象 (new Object());
直接將屬性和方法賦值給 this 對象;
沒有 renturn 語句
請解釋一下 JavaScript 的同源策略
概念:同源策略是客戶端腳本(尤其是Javascript)的重要的安全度量標準。它最早出自Netscape Navigator2.0,其目的是防止某個文檔或腳本從多個不同源裝載。這里的同源策略指的是:協議,域名,端口相同,同源策略是一種安全協議
指一段腳本只能讀取來自同一來源的窗口和文檔的屬性
為什么要有同源限制?
我們舉例說明:比如一個黑客程序,他利用Iframe把真正的銀行登錄頁面嵌到他的頁面上,當你使用真實的用戶名,密碼登錄時,他的頁面就可以通過Javascript讀取到你的表單中input中的內容,這樣用戶名,密碼就輕松到手了。
缺點
現在網站的JS都會進行壓縮,一些文件用了嚴格模式,而另一些沒有。這時這些本來是嚴格模式的文件,被 merge后,這個串就到了文件的中間,不僅沒有指示嚴格模式,反而在壓縮后浪費了字節
實現一個函數clone,可以對JavaScript中的5種主要的數據類型(包括Number、String、Object、Array、Boolean)進行值復制
Object.prototype.clone = function(){ var o = this.constructor === Array ? [] : {}; for(var e in this){ o[e] = typeof this[e] === "object" ? this[e].clone() : this[e]; } return o; }
說說嚴格模式的限制
嚴格模式主要有以下限制:
變量必須聲明后再使用
函數的參數不能有同名屬性,否則報錯
不能使用with語句
不能對只讀屬性賦值,否則報錯
不能使用前綴0表示八進制數,否則報錯
不能刪除不可刪除的屬性,否則報錯
不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop]
eval不會在它的外層作用域引入變量
eval和arguments不能被重新賦值
arguments不會自動反映函數參數的變化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局對象
不能使用fn.caller和fn.arguments獲取函數調用的堆棧
增加了保留字(比如protected、static和interface)
如何刪除一個cookie
將時間設為當前時間往前一點
var date = new Date(); date.setDate(date.getDate() - 1);//真正的刪除
setDate()方法用于設置一個月的某一天
expires的設置
document.cookie = "user="+ encodeURIComponent("name") + ";expires = " + new Date(0)
編寫一個方法 求一個字符串的字節長度
假設:一個英文字符占用一個字節,一個中文字符占用兩個字節
function GetBytes(str){ var len = str.length; var bytes = len; for(var i=0; i255) bytes++; } return bytes; } alert(GetBytes("你好,as"));
請解釋什么是事件代理
事件代理(Event Delegation),又稱之為事件委托。是 JavaScript 中常用綁定事件的常用技巧。顧名思義,“事件代理”即是把原本需要綁定的事件委托給父元素,讓父元素擔當事件監聽的職務。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好處是可以提高性能
attribute和property的區別是什么?
attribute是dom元素在文檔中作為html標簽擁有的屬性;
property就是dom元素在js中作為對象擁有的屬性。
對于html的標準屬性來說,attribute和property是同步的,是會自動更新的
但是對于自定義的屬性來說,他們是不同步的
頁面編碼和被請求的資源編碼如果不一致如何處理?
后端響應頭設置 charset
前端頁面設置 charset
把放在
之前和之后有什么區別?瀏覽器會如何解析它們?
按照HTML標準,在結束后出現或任何元素的開始標簽,都是解析錯誤
雖然不符合HTML標準,但瀏覽器會自動容錯,使實際效果與寫在之前沒有區別
瀏覽器的容錯機制會忽略