天天看點

JavaScript知識點的回顧

JavaScript的組成

  JavaScript由一下三部分組成:

  1. EECMAScript(核心):JavaScript語言基礎
  2. DOM(文檔對象模型):規定了通路HTML和XML的接口
  3. BOM(浏覽器對象模型):提供了浏覽器視窗之間進行互動的對象和方法

js的基本資料類型和引用資料類型

  • 基本資料類型:undefined、null、Boolean、number、string、symbol
  • 引用資料類型:object、array、function

檢測浏覽器版本有哪些方式?

  • 根據navigator.userAgent // UA.toLowerCaser().indexOf('chrome')
  • 根據window對象成員 // ‘ActiveObject’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樹,并行請求css、image、js
  • css檔案下載下傳完成,開始構成CSSOM(CSS樹)
  • CSSOM建構結束後,和DOM一起生成Render Tree(渲染樹)
  • 布局(Layout):計算出每個節點在螢幕中的位置
  • 顯示(Painting):通過顯示卡把頁面畫到螢幕上

  DOM樹和渲染樹的差別:

  1.   DOM樹與HTML标簽一一對應,包括head和隐藏元素
  2. 渲染樹不包括head和隐藏元素,大段文本的每一個行都是獨立節點,每一個節點都有對應的css屬性

重繪和回流(重排)的差別和關系?

  • 重繪:當渲染樹中的元素外觀(如:顔色)發生改變,不影響布局時,産生重繪
  • 回流:當渲染樹中元素的布局(如:尺寸、位置、隐藏)發生改變時,産出重繪回流
  • 注意:js擷取layout屬性值(如:offsetLeft、scrollTop、getComputeStyle)也會引起回流。因為浏覽器需要通過回流計算最新值
  • 回流必将引起重繪,而重繪不一定會引起回流

如何最小化重繪和回流?

  • 需要對元素進行複雜的操作時,可以先隐藏(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作用域:

  1. 在Java、C等語言中,作用域為for語句、if語句或{}内的一塊區域,稱為作用域;
  2. 而在JavaScript中,作用域為function(){}内區域,稱為函數作用域

  JavaScript變量聲明提升:

  1. 在JavaScript中,函數聲明與變量聲明經常被JavaScript引擎隐式地提升到目前作用域的頂部
  2. 聲明語句中的指派部分并不會被提升,隻有名稱被提升
  3. 函數聲明的優先級高于變量,如果變量名和函數名相同且未指派,則函數聲明會覆寫變量聲明
  4. 如果函數有多個同名參數,那麼最後一個參數(即使沒有定義)會覆寫前面的同名參數

介紹JavaScript的原型,原型鍊?有什麼特點?

  原型:

  • JavaScript的所有對象都包含了一個[__proto__]内部屬性,這個屬性所對應的就是該對象的原型
  • JavaScript的函數對象,除了原型[__proto__]之外,還預置了prototype屬性
  • 當函數對象作為構造函數建立執行個體時,該prototype屬性值将被作為執行個體對象的原型[__proto__]

  原型鍊:

  • 當一個對象調用的屬性、方法自身不存在時,就會去自己[__proto__]關聯的前輩prototpe對象上去找
  • 如果沒有找到,就會去該prototype原型[__proto__]關聯的前輩prototype去找。依次類推,知道找到屬性、方法或undefined為止,進而形成了所謂的原型鍊

  原型特點:

    JavaScript對象是通過引用來傳遞的,當修改原型時,與之相關的對象也會繼承這一改變

JavaScript有幾種類型的值

  • 原始資料類型(undefined、null、Boolean、number、string)--棧
  • 引用資料類型(對象、數組和函數)--堆
  • 兩種類型的差別是:存儲位置不同;
  • 原始資料類型是直接存儲在棧(stack)中的簡單資料段,占據空間小、小大固定,屬于被頻繁使用資料;
  • 引用資料類型存儲在堆(heap)中對象,占據空間大、大小不固定,如果存儲在棧中,将會影響呈現運作的性能
  • 引用資料類型在棧中存儲了指針,該指針指向堆中該實體的起始位址。
  • 當解釋器尋找引用值時,會首先檢索其在棧中的位址,取得位址後從堆中獲得實體

JavaScript如何實作一個類,怎麼執行個體化這個類?

  1.構造函數法(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();
           

  2.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();
  };
}
           

  3.極簡主義法(消除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();
           

  4.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如何實作繼承?

  1.構造函數綁定:使用call或apply方法,将父對象的構造函數綁定在子對象上

function Cat(name,color){
 	Animal.apply(this,arguments);
 	this.name = name;
 	this.color = color;
 }
           

  2.執行個體繼承:将子對象的prototype指向父對象的一個執行個體

Cat.prototype = new Animal();
 Cat.prototype.contructor = Cat;
           

  3.拷貝繼承:如果把父對象的所有屬性和方法,拷貝進子對象

function extend(Child,Parent){
 	var p = Parent.prototype;
 	var c = Child.prototype;
 	for (var i in p) {
 		c[i] = p[i]
 	}
 	c.uber = p;
 }
           

  4.原型繼承:将子對象的prototype指向父對象的prototype

function extend(Child,Parent){
 	var F = function(){};
 	F.prototype = Parent.prototype;
 	Child.prototype = new F();
 	Child.prototype.contructor = Child;
 	Child.uber = Parent.prototype
 }
           

  5.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代碼并運作

  1. 應該避免使用eval,不安全,非常耗性能(先解析成js語句,再執行)
  2. 由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)
  1. (sel)// 函數被許多現代浏覽器指令行支援,等價于 document.querySelectorAll(sel)
  2. [].forEach.call(NodeLists)

     // 使用 call 函數将數組周遊函數 forEach 應到節點元素清單
  3. el.style.outline = "1px solid #333"

     // 樣式 outline 位于盒模型之外,不影響元素布局位置
  4. (1<<24)

     // parseInt("ffffff", 16) == 16777215 == 2^24 - 1 // 1<<24 == 2^24 == 16777216
  5. Math.random()*(1<<24)

     // 表示一個位于 0 到 16777216 之間的随機浮點數
  6. ~~Math.random()*(1<<24)

     // 

    ~~

     作用相當于 parseInt 取整
  7. (~~(Math.random()*(1<<24))).toString(16)

     // 轉換為一個十六進制-

JavaScript實作異步程式設計的方法?

  • 回調函數
  • 事件監聽
  • 釋出、訂閱
  • promise對象
  • 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;但寫法有很多種,也能混合使用

  1.對象字面量的方式

person = {firstname:'mark',lastname:'Yun',age:25,eyecolor:'black'}
           

  2.function來模拟無參的構造函數

function Person(){
	var person = new Person();
           

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();
           

  6.用混合方式來建立

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)?為什麼要用它?

  • 閉包是指有權通路另一個函數作用域中變量的函數,建立閉包的最常見的方式就是在一個函數内建立另一個函數,通過另一個函數通路這個函數的局部變量,利用閉包可以突破作用域
  • 閉包的特性:
  1. 函數内再嵌套函數
  2. 内部函數可以引用外層的參數和變量
  3. 參數和變量不會被垃圾回收機制回收

javascript 代碼中的"use strict";是什麼意思 ? 使用它差別是什麼?

  • use strict是一種ECMAscript 5 添加的(嚴格)運作模式,這種模式使得 Javascript 在更嚴格的條件下運作,使JS編碼更加規範化的模式,消除Javascript文法的一些不合理、不嚴謹之處,減少一些怪異行為

同步和異步的差別?

  • 同步:浏覽器通路伺服器請求,使用者看得到頁面重新整理,重新發請求,等請求完,頁面重新整理,新内容出現,使用者看到新内容,進行下一步操作
  • 異步:浏覽器通路伺服器請求,使用者正常操作,浏覽器後端進行請求。等請求完,頁面不重新整理,新内容也會出現,使用者看到新内容

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,唯一性

Ajax 是什麼? 如何建立一個Ajax?

ajax的全稱:Asynchronous Javascript And XML
  • 異步傳輸+js+xml
  • 所謂異步,在這裡簡單地解釋就是:向伺服器發送請求的時候,我們不必等待結果,而是可以同時做其他的事情,等到有了結果它自己會根據設定進行後續操作,與此同時,頁面是不會發生整頁重新整理的,提高了使用者體驗
  • 建立XMLHttpRequest對象,也就是建立一個異步調用對象
  • 建一個新的HTTP請求,并指定該HTTP請求的方法、URL及驗證資訊
  • 設定響應HTTP請求狀态變化的函數
  • 發送HTTP請求
  • 擷取異步調用傳回的資料
  • 用JavaScript和DOM實作局部重新整理

哪些操作會造成記憶體洩漏?

  • 記憶體洩漏指任何對象在您不再擁有或需要它之後仍然存在
  • 垃圾回收器定期掃描對象,并計算引用了每個對象的其他對象的數量。如果一個對象的引用數量為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',,'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() - );//真正的删除
           

setDate()方法用于設定一個月的某一天

  • expires的設定

編寫一個方法 求一個字元串的位元組長度

  • 假設:一個英文字元占用一個位元組,一個中文字元占用兩個位元組
function GetBytes(str){

        var len = str.length;

        var bytes = len;

        for(var i=; i<len; i++){

            if (str.charCodeAt(i) > ) bytes++;

        }

        return bytes;

    }

alert(GetBytes("你好,as"));
           

請解釋什麼是事件代理

  • 事件代理(Event Delegation),又稱之為事件委托。是 JavaScript 中常用綁定事件的常用技巧。顧名思義,“事件代理”即是把原本需要綁定的事件委托給父元素,讓父元素擔當事件監聽的職務。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好處是可以提高性能

attribute和property的差別是什麼?

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

頁面編碼和被請求的資源編碼如果不一緻如何處理?

  • 後端響應頭設定 charset
  • 前端頁面

    <meta>

    設定 charset

<script>

放在

</body>

之前和之後有什麼差別?浏覽器會如何解析它們?

  • 按照HTML标準,在

    </body>

    結束後出現

    <script>

    或任何元素的開始标簽,都是解析錯誤
  • 雖然不符合HTML标準,但浏覽器會自動容錯,使實際效果與寫在

    </body>

    之前沒有差別
  • 浏覽器的容錯機制會忽略<script>之前的

    </body>

    ,視作

    <script>

    仍在 body 體内。省略

    </body>

    </html>

    閉合标簽符合HTML标準,伺服器可以利用這一标準盡可能少輸出内容

延遲加載JS的方式有哪些?

  • 設定

    <script>

    屬性 defer="defer" (腳本将在頁面完成解析時執行)
  • 動态建立 script DOM:document.createElement('script');
  • XmlHttpRequest 腳本注入
  • 延遲加載工具 LazyLoad

異步加載JS的方式有哪些?

  • 設定

    <script>

    屬性 async="async" (一旦腳本可用,則會異步執行)
  • 動态建立 script DOM:document.createElement('script');
  • XmlHttpRequest 腳本注入
  • 異步加載庫 LABjs
  • 子產品加載器 Sea.js

JavaScript 中,調用函數有哪幾種方式?

  • 方法調用模式 Foo.foo(arg1, arg2);
  • 函數調用模式 foo(arg1, arg2);
  • 構造器調用模式 (new Foo())(arg1, arg2);
  • call/applay調用模式 Foo.foo.call(that, arg1, arg2);
  • bind調用模式 Foo.foo.bind(that)(arg1, arg2)();

簡單實作 Function.bind 函數?

if (!Function.prototype.bind) {
    Function.prototype.bind = function(that) {
      var func = this, args = arguments;
      return function() {
        return func.apply(that, Array.prototype.slice.call(args, ));
      }
    }
  }
  // 隻支援 bind 階段的預設參數:
  func.bind(that, arg1, arg2)();

  // 不支援以下調用階段傳入的參數:
  func.bind(that)(arg1, arg2);
           

列舉一下JavaScript數組和對象有哪些原生方法?

  • 數組:
    • arr.concat(arr1, arr2, arrn);
    • arr.join(",");
    • arr.sort(func);
    • arr.pop();
    • arr.push(e1, e2, en);
    • arr.shift();
    • unshift(e1, e2, en);
    • arr.reverse();
    • arr.slice(start, end);
    • arr.splice(index, count, e1, e2, en);
    • arr.indexOf(el);
    • arr.includes(el); // ES6
  • 對象:
    • object.hasOwnProperty(prop);
    • object.propertyIsEnumerable(prop);
    • object.valueOf();
    • object.toString();
    • object.toLocaleString();
    • Class.prototype.isPropertyOf(object);

Array.splice() 與 Array.splice() 的差別?

  • slice -- “讀取”數組指定的元素,不會對原數組進行修改
    • 文法:arr.slice(start, end)
    • start 指定選取開始位置(含)
    • end 指定選取結束位置(不含)
    • splice
      • “操作”數組指定的元素,會修改原數組,傳回被删除的元素
      • 文法:arr.splice(index, count, [insert Elements])
      • index 是操作的起始位置
      • count = 0 插入元素,count > 0 删除元素
      • [insert Elements] 向數組新插入的元素

JavaScript 對象生命周期的了解?

  • 當建立一個對象時,JavaScript 會自動為該對象配置設定适當的記憶體
  • 垃圾回收器定期掃描對象,并計算引用了該對象的其他對象的數量
  • 如果被引用數量為 0,或惟一引用是循環的,那麼該對象的記憶體即可回收

哪些操作會造成記憶體洩漏?

  • JavaScript 記憶體洩露指對象在不需要使用它時仍然存在,導緻占用的記憶體不能使用或回收
  • 未使用 var 聲明的全局變量
  • 閉包函數(Closures)
  • 循環引用(兩個對象互相引用)
  • 控制台日志(console.log)
  • 移除存在綁定事件的DOM元素(IE)

繼續閱讀