js面試題

JavaScript 的組成

JavaScript 由以下三部分組成:

  • ECMAScript(核心):JavaScript 語(yǔ)言基礎(chǔ)
  • DOM(文檔對(duì)象模型):規(guī)定了訪問(wèn) HTML 和 XML 的接口
  • BOM(瀏覽器對(duì)象模型):提供了瀏覽器窗口之間進(jìn)行交互的對(duì)象和方法

JS 的基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型

  • 基本數(shù)據(jù)類(lèi)型:undefined、null、boolean蝙茶、number酣栈、string剧董、symbol
  • 引用數(shù)據(jù)類(lèi)型:object蝠嘉、array灯萍、function

檢測(cè)瀏覽器版本版本有哪些方式跳座?

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

介紹 JS 有哪些內(nèi)置對(duì)象端铛?

  • 數(shù)據(jù)封裝類(lèi)對(duì)象:Object、Array疲眷、Boolean禾蚕、Number、String
  • 其他對(duì)象:Function狂丝、Arguments换淆、Math、Date几颜、RegExp倍试、Error
  • ES6 新增對(duì)象:Symbol、Map蛋哭、Set县习、Promises、Proxy、Reflect

說(shuō)幾條寫(xiě) JavaScript 的基本規(guī)范躁愿?

  • 代碼縮進(jìn)叛本,建議使用“四個(gè)空格”縮進(jìn)
  • 代碼段使用花括號(hào){}包裹
  • 語(yǔ)句結(jié)束使用分號(hào);
  • 變量和函數(shù)在使用前進(jìn)行聲明
  • 以大寫(xiě)字母開(kāi)頭命名構(gòu)造函數(shù),全大寫(xiě)命名常量
  • 規(guī)范定義 JSON 對(duì)象彤钟,補(bǔ)全雙引號(hào)
  • 用{}和[]聲明對(duì)象和數(shù)組

如何編寫(xiě)高性能的 JavaScript来候?

  • 遵循嚴(yán)格模式:"use strict";
  • 將 js 腳本放在頁(yè)面底部,加快渲染頁(yè)面
  • 將 js 腳本將腳本成組打包样勃,減少請(qǐng)求
  • 使用非阻塞方式下載 js 腳本
  • 盡量使用局部變量來(lái)保存全局變量
  • 盡量減少使用閉包
  • 使用 window 對(duì)象屬性方法時(shí)吠勘,省略 window
  • 盡量減少對(duì)象成員嵌套
  • 緩存 DOM 節(jié)點(diǎn)的訪問(wèn)
  • 通過(guò)避免使用 eval() 和 Function() 構(gòu)造器
  • 給 setTimeout() 和 setInterval() 傳遞函數(shù)而不是字符串作為參數(shù)
  • 盡量使用直接量創(chuàng)建對(duì)象和數(shù)組
  • 最小化重繪(repaint)和回流(reflow)

DOM 元素 e 的 e.getAttribute(propName)和 e.propName 有什么區(qū)別和聯(lián)系

  • e.getAttribute()性芬,是標(biāo)準(zhǔn) DOM 操作文檔元素屬性的方法峡眶,具有通用性可在任意文檔上使用,返回元素在源文件中設(shè)置的屬性
  • e.propName 通常是在 HTML 文檔中訪問(wèn)特定元素的特性植锉,瀏覽器解析元素后生成對(duì)應(yīng)對(duì)象(如 a 標(biāo)簽生成 HTMLAnchorElement)辫樱,這些對(duì)象的特性會(huì)根據(jù)特定規(guī)則結(jié)合屬性設(shè)置得到,對(duì)于沒(méi)有對(duì)應(yīng)特性的屬性俊庇,只能使用 getAttribute 進(jìn)行訪問(wèn)
  • e.getAttribute()返回值是源文件中設(shè)置的值狮暑,類(lèi)型是字符串或者 null(有的實(shí)現(xiàn)返回"")
  • e.propName 返回值可能是字符串、布爾值辉饱、對(duì)象搬男、undefined 等
  • 大部分 attribute 與 property 是一一對(duì)應(yīng)關(guān)系,修改其中一個(gè)會(huì)影響另一個(gè)彭沼,如 id缔逛,title 等屬性
  • 一些布爾屬性<input hidden/>的檢測(cè)設(shè)置需要 hasAttribute 和 removeAttribute 來(lái)完成,或者設(shè)置對(duì)應(yīng) property
  • <a href="../index.html">link</a>中 href 屬性姓惑,轉(zhuǎn)換成 property 的時(shí)候需要通過(guò)轉(zhuǎn)換得到完整 URL
  • 一些 attribute 和 property 不是一一對(duì)應(yīng)如:form 控件中<input value="hello"/>對(duì)應(yīng)的是 defaultValue褐奴,修改或設(shè)置 value property 修改的是控件當(dāng)前值,setAttribute 修改 value 屬性不會(huì)改變 value property

offsetWidth/offsetHeight,clientWidth/clientHeight 與 scrollWidth/scrollHeight 的區(qū)別

  • offsetWidth/offsetHeight 返回值包含 content + padding + border于毙,效果與 e.getBoundingClientRect()相同
  • clientWidth/clientHeight 返回值只包含 content + padding敦冬,如果有滾動(dòng)條,也不包含滾動(dòng)條
  • scrollWidth/scrollHeight 返回值包含 content + padding + 溢出內(nèi)容的尺寸

描述瀏覽器的渲染過(guò)程唯沮,DOM 樹(shù)和渲染樹(shù)的區(qū)別脖旱?

瀏覽器的渲染過(guò)程:

  • 解析 HTML 構(gòu)建 DOM(DOM 樹(shù)),并行請(qǐng)求 css/image/js
  • CSS 文件下載完成介蛉,開(kāi)始構(gòu)建 CSSOM(CSS 樹(shù))
  • CSSOM 構(gòu)建結(jié)束后萌庆,和 DOM 一起生成 Render Tree(渲染樹(shù))
  • 布局(Layout):計(jì)算出每個(gè)節(jié)點(diǎn)在屏幕中的位置
  • 顯示(Painting):通過(guò)顯卡把頁(yè)面畫(huà)到屏幕上

DOM 樹(shù) 和 渲染樹(shù) 的區(qū)別:

  • DOM 樹(shù)與 HTML 標(biāo)簽一一對(duì)應(yīng),包括 head 和隱藏元素
  • 渲染樹(shù)不包括 head 和隱藏元素甘耿,大段文本的每一個(gè)行都是獨(dú)立節(jié)點(diǎn)踊兜,每一個(gè)節(jié)點(diǎn)都有對(duì)應(yīng)的 css 屬性

重繪和回流(重排)的區(qū)別和關(guān)系?

  • 重繪:當(dāng)渲染樹(shù)中的元素外觀(如:顏色)發(fā)生改變佳恬,不影響布局時(shí)捏境,產(chǎn)生重繪
  • 回流:當(dāng)渲染樹(shù)中的元素的布局(如:尺寸于游、位置、隱藏/狀態(tài)狀態(tài))發(fā)生改變時(shí)垫言,產(chǎn)生重繪回流
  • 注意:JS 獲取 Layout 屬性值(如:offsetLeft贰剥、scrollTop、getComputedStyle 等)也會(huì)引起回流筷频。因?yàn)闉g覽器需要通過(guò)回流計(jì)算最新值
  • 回流必將引起重繪蚌成,而重繪不一定會(huì)引起回流

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

  • 需要要對(duì)元素進(jìn)行復(fù)雜的操作時(shí)凛捏,可以先隱藏(display:"none")担忧,操作完成后再顯示
  • 需要?jiǎng)?chuàng)建多個(gè) DOM 節(jié)點(diǎn)時(shí),使用 DocumentFragment 創(chuàng)建完后一次性的加入 document
  • 緩存 Layout 屬性值坯癣,如:var left = elem.offsetLeft; 這樣瓶盛,多次使用 left 只產(chǎn)生一次回流
  • 盡量避免用 table 布局(table 元素一旦觸發(fā)回流就會(huì)導(dǎo)致 table 里所有的其它元素回流)
  • 避免使用 css 表達(dá)式(expression),因?yàn)槊看握{(diào)用都會(huì)重新計(jì)算值(包括加載頁(yè)面)
  • 盡量使用 css 屬性簡(jiǎn)寫(xiě)示罗,如:用 border 代替 border-width, border-style, border-color
    批量修改元素樣式:elem.className 和 elem.style.cssText 代替 elem.style.xxx

script 的位置是否會(huì)影響首屏顯示時(shí)間惩猫?

  • 在解析 HTML 生成 DOM 過(guò)程中,js 文件的下載是并行的蚜点,不需要 DOM 處理到 script 節(jié)點(diǎn)轧房。因此,script 的位置不影響首屏顯示的開(kāi)始時(shí)間绍绘。
  • 瀏覽器解析 HTML 是自上而下的線性過(guò)程奶镶,script 作為 HTML 的一部分同樣遵循這個(gè)原則
  • 因此,script 會(huì)延遲 DomContentLoad脯倒,只顯示其上部分首屏內(nèi)容实辑,從而影響首屏顯示的完成時(shí)間

解釋 JavaScript 中的作用域與變量聲明提升?

JavaScript 作用域:

  • 在 Java藻丢、C 等語(yǔ)言中剪撬,作用域?yàn)?for 語(yǔ)句、if 語(yǔ)句或{}內(nèi)的一塊區(qū)域悠反,稱為作用域残黑;
  • 而在 JavaScript 中,作用域?yàn)?function(){}內(nèi)的區(qū)域斋否,稱為函數(shù)作用域梨水。

JavaScript 變量聲明提升:

  • 在 JavaScript 中,函數(shù)聲明與變量聲明經(jīng)常被 JavaScript 引擎隱式地提升到當(dāng)前作用域的頂部茵臭。
  • 聲明語(yǔ)句中的賦值部分并不會(huì)被提升疫诽,只有名稱被提升
  • 函數(shù)聲明的優(yōu)先級(jí)高于變量,如果變量名跟函數(shù)名相同且未賦值,則函數(shù)聲明會(huì)覆蓋變量聲明
  • 如果函數(shù)有多個(gè)同名參數(shù)奇徒,那么最后一個(gè)參數(shù)(即使沒(méi)有定義)會(huì)覆蓋前面的同名參數(shù)

介紹 JavaScript 的原型雏亚,原型鏈?有什么特點(diǎn)摩钙?

原型:

  • JavaScript 的所有對(duì)象中都包含了一個(gè) [proto] 內(nèi)部屬性罢低,這個(gè)屬性所對(duì)應(yīng)的就是該對(duì)象的原型
  • JavaScript 的函數(shù)對(duì)象,除了原型 [proto] 之外胖笛,還預(yù)置了 prototype 屬性
  • 當(dāng)函數(shù)對(duì)象作為構(gòu)造函數(shù)創(chuàng)建實(shí)例時(shí)网持,該 prototype 屬性值將被作為實(shí)例對(duì)象的原型 [proto]。

原型鏈:

  • 當(dāng)一個(gè)對(duì)象調(diào)用的屬性/方法自身不存在時(shí)长踊,就會(huì)去自己 [proto] 關(guān)聯(lián)的前輩 prototype 對(duì)象上去找
  • 如果沒(méi)找到功舀,就會(huì)去該 prototype 原型 [proto] 關(guān)聯(lián)的前輩 prototype 去找日杈。依次類(lèi)推,直到找到屬性/方法或 undefined 為止酿炸。從而形成了所謂的“原型鏈”

原型特點(diǎn):

  • JavaScript 對(duì)象是通過(guò)引用來(lái)傳遞的,當(dāng)修改原型時(shí)麦萤,與之相關(guān)的對(duì)象也會(huì)繼承這一改變

JavaScript 有幾種類(lèi)型的值扁眯?壮莹,你能畫(huà)一下他們的內(nèi)存圖嗎

  • 原始數(shù)據(jù)類(lèi)型(Undefined,Null姻檀,Boolean命满,Number、String)-- 棧
  • 引用數(shù)據(jù)類(lèi)型(對(duì)象绣版、數(shù)組和函數(shù))-- 堆
  • 兩種類(lèi)型的區(qū)別是:存儲(chǔ)位置不同:
  • 原始數(shù)據(jù)類(lèi)型是直接存儲(chǔ)在棧(stack)中的簡(jiǎn)單數(shù)據(jù)段胶台,占據(jù)空間小、大小固定杂抽,屬于被頻繁使用數(shù)據(jù)诈唬;
  • 引用數(shù)據(jù)類(lèi)型存儲(chǔ)在堆(heap)中的對(duì)象,占據(jù)空間大缩麸、大小不固定铸磅,如果存儲(chǔ)在棧中,將會(huì)影響程序運(yùn)行的性能;
  • 引用數(shù)據(jù)類(lèi)型在棧中存儲(chǔ)了指針阅仔,該指針指向堆中該實(shí)體的起始地址济竹。
  • 當(dāng)解釋器尋找引用值時(shí),會(huì)首先檢索其在棧中的地址霎槐,取得地址后從堆中獲得實(shí)體袭景。

JavaScript 如何實(shí)現(xiàn)一個(gè)類(lèi),怎么實(shí)例化這個(gè)類(lèi)与殃?

  1. 構(gòu)造函數(shù)法(this + prototype) -- 用 new 關(guān)鍵字 生成實(shí)例對(duì)象
    • 缺點(diǎn):用到了 this 和 prototype,編寫(xiě)復(fù)雜爽篷,可讀性差
  function Mobile(name, price){
     this.name = name;
     this.price = price;
   }
   Mobile.prototype.sell = function(){
      alert(this.name + ",售價(jià) $" + this.price);
   }
   var iPhone7 = new Mobile("iPhone7", 1000);
   iPhone7.sell();
  1. Object.create 法 -- 用 Object.create() 生成實(shí)例對(duì)象
    • 缺點(diǎn):不能實(shí)現(xiàn)私有屬性和私有方法,實(shí)例對(duì)象之間也不能共享數(shù)據(jù)
 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();
  };
 }
  1. 極簡(jiǎn)主義法(消除 this 和 prototype) -- 調(diào)用 createNew() 得到實(shí)例對(duì)象
    • 優(yōu)點(diǎn):容易理解,結(jié)構(gòu)清晰優(yōu)雅对途,符合傳統(tǒng)的"面向?qū)ο缶幊?的構(gòu)造
 var Cat = {
   age: 3, // 共享數(shù)據(jù) -- 定義在類(lèi)對(duì)象內(nèi)惶洲,createNew() 外
   createNew: function () {
     var cat = {};
     // var cat = Animal.createNew(); // 繼承 Animal 類(lèi)
     cat.name = "小咪";
     var sound = "喵喵喵"; // 私有屬性--定義在 createNew() 內(nèi),輸出對(duì)象外
     cat.makeSound = function () {
       alert(sound);  // 暴露私有屬性
     };
     cat.changeAge = function(num){
       Cat.age = num; // 修改共享數(shù)據(jù)
     };
     return cat; // 輸出對(duì)象
   }
 };

 var cat = Cat.createNew();
 cat.makeSound();
  1. ES6 語(yǔ)法糖 class -- 用 new 關(guān)鍵字 生成實(shí)例對(duì)象
     class Point {
       constructor(x, y) {
         this.x = x;
         this.y = y;
       }
       toString() {
         return '(' + this.x + ', ' + this.y + ')';
       }
     }

  var point = new Point(2, 3);

Javascript 如何實(shí)現(xiàn)繼承?

  1. 構(gòu)造函數(shù)綁定:使用 call 或 apply 方法钠惩,將父對(duì)象的構(gòu)造函數(shù)綁定在子對(duì)象上
function Cat(name,color){
  Animal.apply(this, arguments);
  this.name = name;
  this.color = color;
}
  1. 實(shí)例繼承:將子對(duì)象的 prototype 指向父對(duì)象的一個(gè)實(shí)例
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
  1. 拷貝繼承:如果把父對(duì)象的所有屬性和方法,拷貝進(jìn)子對(duì)象
function extend(Child, Parent) {
   var p = Parent.prototype;
   var c = Child.prototype;
   for (var i in p) {
      c[i] = p[i];
   }
   c.uber = p;
}
  1. 原型繼承:將子對(duì)象的 prototype 指向父對(duì)象的 prototype
function extend(Child, Parent) {
    var F = function(){};
     F.prototype = Parent.prototype;
     Child.prototype = new F();
     Child.prototype.constructor = Child;
     Child.uber = Parent.prototype;
}
  1. ES6 語(yǔ)法糖 extends:class ColorPoint extends Point {}
class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y); // 調(diào)用父類(lèi)的constructor(x, y)
        this.color = color;
    }
    toString() {
        return this.color + ' ' + super.toString(); // 調(diào)用父類(lèi)的toString()
    }
}

js 繼承方式及其優(yōu)缺點(diǎn)

原型鏈繼承的缺點(diǎn)

  • 一是字面量重寫(xiě)原型會(huì)中斷關(guān)系,使用引用類(lèi)型的原型沐寺,并且子類(lèi)型還無(wú)法給超類(lèi)型傳遞參數(shù)。

借用構(gòu)造函數(shù)(類(lèi)式繼承)

  • 借用構(gòu)造函數(shù)雖然解決了剛才兩種問(wèn)題,但沒(méi)有原型,則復(fù)用無(wú)從談起逛万。所以我們需要原型鏈+借用構(gòu)造函數(shù)的模式,這種模式稱為組合繼承

組合式繼承

  • 組合式繼承是比較常用的一種繼承方法指郁,其背后的思路是使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承疫粥,而通過(guò)借用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。這樣慷彤,既通過(guò)在原型上定義方法實(shí)現(xiàn)了函數(shù)復(fù)用,又保證每個(gè)實(shí)例都有它自己的屬性艘虎。

javascript 創(chuàng)建對(duì)象的幾種方式?

javascript 創(chuàng)建對(duì)象簡(jiǎn)單的說(shuō),無(wú)非就是使用內(nèi)置對(duì)象或各種自定義對(duì)象候生,當(dāng)然還可以用 JSON;但寫(xiě)法有很多種目溉,也能混合使用

  1. 對(duì)象字面量的方式
person={firstname:"Mark",lastname:"Yun",age:25,eyecolor:"black"};
  1. 用 function 來(lái)模擬無(wú)參的構(gòu)造函數(shù)
 function Person(){}
    var person=new Person();//定義一個(gè)function,如果使用new"實(shí)例化",該function可以看作是一個(gè)Class
        person.name="Mark";
        person.age="25";
        person.work=function(){
        alert(person.name+" hello...");
    }
person.work();
  1. 用 function 來(lái)模擬參構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)(用 this 關(guān)鍵字定義構(gòu)造的上下文屬性)
function Pet(name,age,hobby){
    this.name=name;//this作用域:當(dāng)前對(duì)象
    this.age=age;
    this.hobby=hobby;
    this.eat=function(){
        alert("我叫"+this.name+",我喜歡"+this.hobby+",是個(gè)程序員");
    }
}
var maidou =new Pet("麥兜",25,"coding");//實(shí)例化、創(chuàng)建對(duì)象
maidou.eat();//調(diào)用eat方法
  1. 用工廠方式來(lái)創(chuàng)建(內(nèi)置對(duì)象)
var wcDog =new Object();
     wcDog.name="旺財(cái)";
     wcDog.age=3;
wcDog.work=function(){
    alert("我是"+wcDog.name+",汪汪汪......");
}
wcDog.work();
  1. 用原型方式來(lái)創(chuàng)建
function Dog(){

    }
Dog.prototype.name="旺財(cái)";
Dog.prototype.eat=function(){
    alert(this.name+"是個(gè)吃貨");
}
var wangcai =new Dog();
wangcai.eat();
  1. 用混合方式來(lái)創(chuàng)建
function Car(name,price){
    this.name=name;
    this.price=price;
}
    Car.prototype.sell=function(){
    alert("我是"+this.name+",我現(xiàn)在賣(mài)"+this.price+"萬(wàn)元");
    }
var camry =new Car("凱美瑞",27);
camry.sell();

Javascript 作用鏈域?

  • 全局函數(shù)無(wú)法查看局部函數(shù)的內(nèi)部細(xì)節(jié)娇未,但局部函數(shù)可以查看其上層的函數(shù)細(xì)節(jié)飒房,直至全局細(xì)節(jié)
  • 如果當(dāng)前作用域沒(méi)有找到屬性或方法,會(huì)向上層作用域查找嚼松,直至全局函數(shù)献酗,這種形式就是作用域鏈

談?wù)?this 對(duì)象的理解

  • this 總是指向函數(shù)的直接調(diào)用者
  • 如果有 new 關(guān)鍵字罕偎,this 指向 new 出來(lái)的實(shí)例對(duì)象
  • 在事件中,this 指向觸發(fā)這個(gè)事件的對(duì)象
  • IE 下 attachEvent 中的 this 總是指向全局對(duì)象 Window

eval 是做什么的俏站?

eval 的功能是把對(duì)應(yīng)的字符串解析成 JS 代碼并運(yùn)行

  • 應(yīng)該避免使用 eval,不安全犯祠,非常耗性能(先解析成 js 語(yǔ)句,再執(zhí)行)
  • 由 JSON 字符串轉(zhuǎn)換為 JSON 對(duì)象的時(shí)候可以用 eval('('+ str +')');

什么是 Window 對(duì)象? 什么是 Document 對(duì)象?

  • Window 對(duì)象表示當(dāng)前瀏覽器的窗口,是 JavaScript 的頂級(jí)對(duì)象藤乙。
  • 我們創(chuàng)建的所有對(duì)象、函數(shù)划咐、變量都是 Window 對(duì)象的成員政鼠。
  • Window 對(duì)象的方法和屬性是在全局范圍內(nèi)有效的。
  • Document 對(duì)象是 HTML 文檔的根節(jié)點(diǎn)與所有其他節(jié)點(diǎn)(元素節(jié)點(diǎn)官帘,文本節(jié)點(diǎn),屬性節(jié)點(diǎn), 注釋節(jié)點(diǎn))
  • Document 對(duì)象使我們可以通過(guò)腳本對(duì) HTML 頁(yè)面中的所有元素進(jìn)行訪問(wèn)
  • Document 對(duì)象是 Window 對(duì)象的一部分涌哲,可通過(guò) window.document 屬性對(duì)其進(jìn)行訪問(wèn)

介紹 DOM 的發(fā)展

  • DOM:文檔對(duì)象模型(Document Object Model),定義了訪問(wèn) HTML 和 XML 文檔的標(biāo)準(zhǔn)稍刀,與編程語(yǔ)言及平臺(tái)無(wú)關(guān)
  • DOM0:提供了查詢和操作 Web 文檔的內(nèi)容 API澳迫。未形成標(biāo)準(zhǔn)抓歼,實(shí)現(xiàn)混亂谣妻。如:document.forms['login']
  • DOM1:W3C 提出標(biāo)準(zhǔn)化的 DOM,簡(jiǎn)化了對(duì)文檔中任意部分的訪問(wèn)和操作减江。如:JavaScript 中的 Document 對(duì)象
  • DOM2:原來(lái) DOM 基礎(chǔ)上擴(kuò)充了鼠標(biāo)事件等細(xì)分模塊份企,增加了對(duì) CSS 的支持。如:getComputedStyle(elem, pseudo)
  • DOM3:增加了 XPath 模塊和加載與保存(Load and Save)模塊俐芯。如:XPathEvaluator

介紹 DOM0,DOM2,DOM3 事件處理方式區(qū)別

DOM0 級(jí)事件處理方式:

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

DOM2 級(jí)事件處理方式:

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

DOM3 級(jí)事件處理方式:

  • eventUtil.addListener(input, "textInput", func);
  • eventUtil 是自定義對(duì)象钞脂,textInput 是 DOM3 級(jí)事件

事件的三個(gè)階段

捕獲、目標(biāo)、冒泡

介紹事件“捕獲”和“冒泡”執(zhí)行順序和事件的執(zhí)行次數(shù)扇调?

按照 W3C 標(biāo)準(zhǔn)的事件:首是進(jìn)入捕獲階段,直到達(dá)到目標(biāo)元素,再進(jìn)入冒泡階段

事件執(zhí)行次數(shù)(DOM2-addEventListener):元素上綁定事件的個(gè)數(shù)

  • 注意 1:前提是事件被確實(shí)觸發(fā)
  • 注意 2:事件綁定幾次就算幾個(gè)事件,即使類(lèi)型和功能完全一樣也不會(huì)“覆蓋”

事件執(zhí)行順序:判斷的關(guān)鍵是否目標(biāo)元素

  • 非目標(biāo)元素:根據(jù) W3C 的標(biāo)準(zhǔn)執(zhí)行:捕獲->目標(biāo)元素->冒泡(不依據(jù)事件綁定順序)
  • 目標(biāo)元素:依據(jù)事件綁定順序:先綁定的事件先執(zhí)行(不依據(jù)捕獲冒泡標(biāo)準(zhǔn))
  • 最終順序:父元素捕獲->目標(biāo)元素事件 1->目標(biāo)元素事件 2->子元素捕獲->子元素冒泡->父元素冒泡
  • 注意:子元素事件執(zhí)行前提 事件確實(shí)“落”到子元素布局區(qū)域上曼库,而不是簡(jiǎn)單的具有嵌套關(guān)系

在一個(gè) DOM 上同時(shí)綁定兩個(gè)點(diǎn)擊事件:一個(gè)用捕獲,一個(gè)用冒泡种玛。事件會(huì)執(zhí)行幾次,先執(zhí)行冒泡還是捕獲祭示?

  • 該 DOM 上的事件如果被觸發(fā)掰担,會(huì)執(zhí)行兩次(執(zhí)行次數(shù)等于綁定次數(shù))
  • 如果該 DOM 是目標(biāo)元素,則按事件綁定順序執(zhí)行月趟,不區(qū)分冒泡/捕獲
  • 如果該 DOM 是處于事件流中的非目標(biāo)元素耕肩,則先執(zhí)行捕獲婚被,后執(zhí)行冒泡

事件的代理/委托

事件委托是指將事件綁定目標(biāo)元素的到父元素上谷炸,利用冒泡機(jī)制觸發(fā)該事件

優(yōu)點(diǎn):

  • 可以減少事件注冊(cè)拓颓,節(jié)省大量?jī)?nèi)存占用
  • 可以將事件應(yīng)用于動(dòng)態(tài)添加的子元素上

缺點(diǎn): 使用不當(dāng)會(huì)造成事件在不應(yīng)該觸發(fā)時(shí)觸發(fā)

示例:

ulEl.addEventListener('click', function(e){
    var target = event.target || event.srcElement;
    if(!!target && target.nodeName.toUpperCase() === "LI"){
        console.log(target.innerHTML);
    }
}, false);

IE 與火狐的事件機(jī)制有什么區(qū)別场航? 如何阻止冒泡蜜另?

IE 只事件冒泡捣辆,不支持事件捕獲耸序;火狐同時(shí)支持件冒泡和事件捕獲坎怪。

阻止冒泡:

  • 取消默認(rèn)操作: w3c 的方法是 e.preventDefault()罢坝,IE 則是使用 e.returnValue = false;
  • return false javascript 的 return false 只會(huì)阻止默認(rèn)行為,而是用 jQuery 的話則既阻止默認(rèn)行為又防止對(duì)象冒泡搅窿。
  • 阻止冒泡 w3c 的方法是 e.stopPropagation()嘁酿,IE 則是使用 e.cancelBubble = true
[js] view plaincopy
function stopHandler(event)

    window.event?window.event.cancelBubble=true:event.stopPropagation();

}

參考鏈接:淺談 javascript 事件取消和阻止冒泡-開(kāi)源中國(guó) 2015

IE 的事件處理和 W3C 的事件處理有哪些區(qū)別?(必考)

綁定事件

  • W3C: targetEl.addEventListener('click', handler, false);
  • IE: targetEl.attachEvent('onclick', handler);

刪除事件

  • W3C: targetEl.removeEventListener('click', handler, false);
  • IE: targetEl.detachEvent(event, handler);

事件對(duì)象

  • W3C: var e = arguments.callee.caller.arguments[0]
  • IE: window.event

事件目標(biāo)

  • W3C: e.target
  • IE: window.event.srcElement

阻止事件默認(rèn)行為

  • W3C: e.preventDefault()
  • IE: window.event.returnValue = false'

阻止事件傳播

  • W3C: e.stopPropagation()
  • IE: window.event.cancelBubble = true

W3C 事件的 target 與 currentTarget 的區(qū)別男应?

  • target 只會(huì)出現(xiàn)在事件流的目標(biāo)階段
  • currentTarget 可能出現(xiàn)在事件流的任何階段
  • 當(dāng)事件流處在目標(biāo)階段時(shí)恩溅,二者的指向相同
  • 當(dāng)事件流處于捕獲或冒泡階段時(shí):currentTarget 指向當(dāng)前事件活動(dòng)的對(duì)象(一般為父級(jí))

如何派發(fā)事件(dispatchEvent)捡遍?(如何進(jìn)行事件廣播芹关?)

  • 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);
    }
}

什么是函數(shù)節(jié)流功偿?介紹一下應(yīng)用場(chǎng)景和原理关拒?

  • 函數(shù)節(jié)流(throttle)是指阻止一個(gè)函數(shù)在很短時(shí)間間隔內(nèi)連續(xù)調(diào)用。 只有當(dāng)上一次函數(shù)執(zhí)行后達(dá)到規(guī)定的時(shí)間間隔疆液,才能進(jìn)行下一次調(diào)用。 但要保證一個(gè)累計(jì)最小調(diào)用間隔(否則拖拽類(lèi)的節(jié)流都將無(wú)連續(xù)效果)
  • 函數(shù)節(jié)流用于 onresize, onscroll 等短時(shí)間內(nèi)會(huì)多次觸發(fā)的事件
  • 函數(shù)節(jié)流的原理:使用定時(shí)器做時(shí)間節(jié)流。 當(dāng)觸發(fā)一個(gè)事件時(shí),先用 setTimout 讓這個(gè)事件延遲一小段時(shí)間再執(zhí)行。 如果在這個(gè)時(shí)間間隔內(nèi)又觸發(fā)了事件挎春,就 clearTimeout 原來(lái)的定時(shí)器丰涉, 再 setTimeout 一個(gè)新的定時(shí)器重復(fù)以上流程加袋。

函數(shù)節(jié)流簡(jiǎn)單實(shí)現(xiàn):

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

區(qū)分什么是“客戶區(qū)坐標(biāo)”、“頁(yè)面坐標(biāo)”、“屏幕坐標(biāo)”邻耕?

  • 客戶區(qū)坐標(biāo):鼠標(biāo)指針在可視區(qū)中的水平坐標(biāo)(clientX)和垂直坐標(biāo)(clientY)
  • 頁(yè)面坐標(biāo):鼠標(biāo)指針在頁(yè)面布局中的水平坐標(biāo)(pageX)和垂直坐標(biāo)(pageY)
  • 屏幕坐標(biāo):設(shè)備物理屏幕的水平坐標(biāo)(screenX)和垂直坐標(biāo)(screenY)

如何獲得一個(gè) DOM 元素的絕對(duì)位置柳爽?

  • elem.offsetLeft:返回元素相對(duì)于其定位父級(jí)左側(cè)的距離
  • elem.offsetTop:返回元素相對(duì)于其定位父級(jí)頂部的距離
  • elem.getBoundingClientRect():返回一個(gè) DOMRect 對(duì)象,包含一組描述邊框的只讀屬性核无,單位像素

分析 ['1', '2', '3'].map(parseInt) 答案是多少术幔?(嘲┮希考)

答案:[1, NaN, NaN]

parseInt(string, radix) 第 2 個(gè)參數(shù) radix 表示進(jìn)制咬摇。省略 radix 或 radix = 0,則數(shù)字將以十進(jìn)制解析

map 每次為 parseInt 傳 3 個(gè)參數(shù)(elem, index, array),其中 index 為數(shù)組索引

因此享完,map 遍歷 ["1", "2", "3"]猜旬,相應(yīng) parseInt 接收參數(shù)如下

parseInt('1', 0);  // 1
parseInt('2', 1);  // NaN
parseInt('3', 2);  // NaN

所以脆栋,parseInt 參數(shù) radix 不合法,導(dǎo)致返回值為 NaN

new 操作符具體干了什么洒擦?

  • 創(chuàng)建實(shí)例對(duì)象椿争,this 變量引用該對(duì)象,同時(shí)還繼承了構(gòu)造函數(shù)的原型
  • 屬性和方法被加入到 this 引用的對(duì)象中
  • 新創(chuàng)建的對(duì)象由 this 所引用熟嫩,并且最后隱式的返回 this

用原生 JavaScript 的實(shí)現(xiàn)過(guò)什么功能嗎秦踪?

封裝選擇器、調(diào)用第三方 API掸茅、設(shè)置和獲取樣式(自由回答)

解釋一下這段代碼的意思嗎椅邓?

  [].forEach.call($$("*"), function(el){
      el.style.outline = "1px solid #" + (~~(Math.random()*(1<<24))).toString(16);
  })

解釋?zhuān)韩@取頁(yè)面所有的元素,遍歷這些元素昧狮,為它們添加 1 像素隨機(jī)顏色的輪廓(outline)

  • (sel) //函數(shù)被許多現(xiàn)代瀏覽器命令行支持景馁,等價(jià)于 document.querySelectorAll(sel)
  • [].forEach.call(NodeLists) // 使用 call 函數(shù)將數(shù)組遍歷函數(shù) forEach 應(yīng)到節(jié)點(diǎn)元素列表
  • 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) // 表示一個(gè)位于 0 到 16777216 之間的隨機(jī)浮點(diǎn)數(shù)
  • ~~Math.random()*(1<<24) // ~~ 作用相當(dāng)于 parseInt 取整
  • (~~(Math.random()*(1<<24))).toString(16) // 轉(zhuǎn)換為一個(gè)十六進(jìn)制-

JavaScript 實(shí)現(xiàn)異步編程的方法逗鸣?

  • 回調(diào)函數(shù)
  • 事件監(jiān)聽(tīng)
  • 發(fā)布/訂閱
  • Promises 對(duì)象
  • Async 函數(shù)[ES7]

web 開(kāi)發(fā)中會(huì)話跟蹤的方法有哪些

  • cookie
  • session
  • url 重寫(xiě)
  • 隱藏 input
  • ip 地址

什么是閉包(closure)裁僧,為什么要用它?

閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中變量的函數(shù)慕购,創(chuàng)建閉包的最常見(jiàn)的方式就是在一個(gè)函數(shù)內(nèi)創(chuàng)建另一個(gè)函數(shù),通過(guò)另一個(gè)函數(shù)訪問(wèn)這個(gè)函數(shù)的局部變量,利用閉包可以突破作用鏈域

閉包的特性:

  • 函數(shù)內(nèi)再嵌套函數(shù)
  • 內(nèi)部函數(shù)可以引用外層的參數(shù)和變量
  • 參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收

javascript 代碼中的"use strict";是什么意思 ? 使用它區(qū)別是什么茬底?

use strict 是一種 ECMAscript 5 添加的(嚴(yán)格)運(yùn)行模式,這種模式使得 Javascript 在更嚴(yán)格的條件下運(yùn)行,使 JS 編碼更加規(guī)范化的模式,消除 Javascript 語(yǔ)法的一些不合理沪悲、不嚴(yán)謹(jǐn)之處,減少一些怪異行為

如何判斷一個(gè)對(duì)象是否屬于某個(gè)類(lèi)阱表?

// 使用instanceof (待完善)
   if(a instanceof Person){
       alert('yes');
   }

js 延遲加載的方式有哪些殿如?

defer 和 async、動(dòng)態(tài)創(chuàng)建 DOM 方式(用得最多)最爬、按需異步載入 js

defer 和 async

defer 并行加載 js 文件涉馁,會(huì)按照頁(yè)面上 script 標(biāo)簽的順序執(zhí)行 async 并行加載 js 文件,下載完成立即執(zhí)行爱致,不會(huì)按照頁(yè)面上 script 標(biāo)簽的順序執(zhí)行

Ajax 是什么? 如何創(chuàng)建一個(gè) Ajax烤送?

ajax 的全稱:Asynchronous Javascript And XML

異步傳輸+js+xml

所謂異步,在這里簡(jiǎn)單地解釋就是:向服務(wù)器發(fā)送請(qǐng)求的時(shí)候糠悯,我們不必等待結(jié)果帮坚,而是可以同時(shí)做其他的事情妻往,等到有了結(jié)果它自己會(huì)根據(jù)設(shè)定進(jìn)行后續(xù)操作,與此同時(shí)试和,頁(yè)面是不會(huì)發(fā)生整頁(yè)刷新的讯泣,提高了用戶體驗(yàn)

  • 創(chuàng)建 XMLHttpRequest 對(duì)象,也就是創(chuàng)建一個(gè)異步調(diào)用對(duì)象
  • 建一個(gè)新的 HTTP 請(qǐng)求,并指定該 HTTP 請(qǐng)求的方法、URL 及驗(yàn)證信息
  • 設(shè)置響應(yīng) HTTP 請(qǐng)求狀態(tài)變化的函數(shù)
  • 發(fā)送 HTTP 請(qǐng)求
  • 獲取異步調(diào)用返回的數(shù)據(jù)
  • 用 JavaScript 和 DOM 實(shí)現(xiàn)局部刷新

同步和異步的區(qū)別?

  • 同步:瀏覽器訪問(wèn)服務(wù)器請(qǐng)求阅悍,用戶看得到頁(yè)面刷新好渠,重新發(fā)請(qǐng)求,等請(qǐng)求完,頁(yè)面刷新,新內(nèi)容出現(xiàn)抛人,用戶看到新內(nèi)容,進(jìn)行下一步操作
  • 異步:瀏覽器訪問(wèn)服務(wù)器請(qǐng)求择葡,用戶正常操作,瀏覽器后端進(jìn)行請(qǐng)求晌畅。等請(qǐng)求完,頁(yè)面不刷新寡痰,新內(nèi)容也會(huì)出現(xiàn)抗楔,用戶看到新內(nèi)容

documen.write 和 innerHTML 的區(qū)別

  • document.write 只能重繪整個(gè)頁(yè)面
  • innerHTML 可以重繪頁(yè)面的一部分

DOM 操作——怎樣添加、移除拦坠、移動(dòng)连躏、復(fù)制、創(chuàng)建和查找節(jié)點(diǎn)?

創(chuàng)建新節(jié)點(diǎn)

  • createDocumentFragment() //創(chuàng)建一個(gè) DOM 片段
  • createElement() //創(chuàng)建一個(gè)具體的元素
  • createTextNode() //創(chuàng)建一個(gè)文本節(jié)點(diǎn)

添加贞滨、移除入热、替換、插入

  • appendChild()
  • removeChild()
  • replaceChild()
  • insertBefore() //在已有的子節(jié)點(diǎn)前插入一個(gè)新的子節(jié)點(diǎn)

查找

  • getElementsByTagName() //通過(guò)標(biāo)簽名稱
  • getElementsByName() // 通過(guò)元素的 Name 屬性的值(IE 容錯(cuò)能力較強(qiáng)晓铆,會(huì)得到一個(gè)數(shù)組勺良,其中包括 id 等于 name 值的) * getElementById() //通過(guò)元素 Id,唯一性

那些操作會(huì)造成內(nèi)存泄漏骄噪?

  • 內(nèi)存泄漏指任何對(duì)象在您不再擁有或需要它之后仍然存在
  • 垃圾回收器定期掃描對(duì)象尚困,并計(jì)算引用了每個(gè)對(duì)象的其他對(duì)象的數(shù)量。如果一個(gè)對(duì)象的引用數(shù)量為 0(沒(méi)有其他對(duì)象引用過(guò)該對(duì)象)链蕊,或?qū)υ搶?duì)象的惟一引用是循環(huán)的事甜,那么該對(duì)象的內(nèi)存即可回收
  • setTimeout 的第一個(gè)參數(shù)使用字符串而非函數(shù)的話,會(huì)引發(fā)內(nèi)存泄漏
  • 閉包滔韵、控制臺(tái)日志逻谦、循環(huán)(在兩個(gè)對(duì)象彼此引用且彼此保留時(shí),就會(huì)產(chǎn)生一個(gè)循環(huán))

漸進(jìn)增強(qiáng)和優(yōu)雅降級(jí)

  • 漸進(jìn)增強(qiáng) :針對(duì)低版本瀏覽器進(jìn)行構(gòu)建頁(yè)面陪蜻,保證最基本的功能邦马,然后再針對(duì)高級(jí)瀏覽器進(jìn)行效果、交互等改進(jìn)和追加功能達(dá)到更好的用戶體驗(yàn)。
  • 優(yōu)雅降級(jí) :一開(kāi)始就構(gòu)建完整的功能勇婴,然后再針對(duì)低版本瀏覽器進(jìn)行兼容

Javascript 垃圾回收方法

標(biāo)記清除(mark and sweep)

  • 這是 JavaScript 最常見(jiàn)的垃圾回收方式忱嘹,當(dāng)變量進(jìn)入執(zhí)行環(huán)境的時(shí)候,比如函數(shù)中聲明一個(gè)變量耕渴,垃圾回收器將其標(biāo)記為“進(jìn)入環(huán)境”拘悦,當(dāng)變量離開(kāi)環(huán)境的時(shí)候(函數(shù)執(zhí)行結(jié)束)將其標(biāo)記為“離開(kāi)環(huán)境”
  • 垃圾回收器會(huì)在運(yùn)行的時(shí)候給存儲(chǔ)在內(nèi)存中的所有變量加上標(biāo)記,然后去掉環(huán)境中的變量以及被環(huán)境中變量所引用的變量(閉包)橱脸,在這些完成之后仍存在標(biāo)記的就是要?jiǎng)h除的變量了

引用計(jì)數(shù)(reference counting)

  • 在低版本 IE 中經(jīng)常會(huì)出現(xiàn)內(nèi)存泄露础米,很多時(shí)候就是因?yàn)槠洳捎靡糜?jì)數(shù)方式進(jìn)行垃圾回收。引用計(jì)數(shù)的策略是跟蹤記錄每個(gè)值被使用的次數(shù)添诉,當(dāng)聲明了一個(gè) 變量并將一個(gè)引用類(lèi)型賦值給該變量的時(shí)候這個(gè)值的引用次數(shù)就加 1屁桑,如果該變量的值變成了另外一個(gè),則這個(gè)值得引用次數(shù)減 1栏赴,當(dāng)這個(gè)值的引用次數(shù)變?yōu)?0 的時(shí) 候蘑斧,說(shuō)明沒(méi)有變量在使用,這個(gè)值沒(méi)法被訪問(wèn)了须眷,因此可以將其占用的空間回收竖瘾,這樣垃圾回收器會(huì)在運(yùn)行的時(shí)候清理掉引用次數(shù)為 0 的值占用的空間

參考鏈接 內(nèi)存管理-MDN

用過(guò)哪些設(shè)計(jì)模式?

  1. 工廠模式:
  • 主要好處就是可以消除對(duì)象間的耦合花颗,通過(guò)使用工程方法而不是 new 關(guān)鍵字捕传。將所有實(shí)例化的代碼集中在一個(gè)位置防止代碼重復(fù)
  • 工廠模式解決了重復(fù)實(shí)例化的問(wèn)題 ,但還有一個(gè)問(wèn)題,那就是識(shí)別問(wèn)題扩劝,因?yàn)楦緹o(wú)法 搞清楚他們到底是哪個(gè)對(duì)象的實(shí)例
function createObject(name,age,profession){
    //集中實(shí)例化的函數(shù)
    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');//第一個(gè)實(shí)例var test2 = createObject('mike',25,'engineer');//第二個(gè)實(shí)例
  1. 構(gòu)造函數(shù)模式
  • 使用構(gòu)造函數(shù)的方法 庸论,即解決了重復(fù)實(shí)例化的問(wèn)題 ,又解決了對(duì)象識(shí)別的問(wèn)題棒呛,該模式與工廠模式的不同之處在于
  • 構(gòu)造函數(shù)方法沒(méi)有顯示的創(chuàng)建對(duì)象 (new Object());
  • 直接將屬性和方法賦值給 this 對(duì)象;
  • 沒(méi)有 renturn 語(yǔ)句

說(shuō)說(shuō)你對(duì)閉包的理解

使用閉包主要是為了設(shè)計(jì)私有的方法和變量聂示。閉包的優(yōu)點(diǎn)是可以避免全局變量的污染,缺點(diǎn)是閉包會(huì)常駐內(nèi)存簇秒,會(huì)增大內(nèi)存使用量鱼喉,使用不當(dāng)很容易造成內(nèi)存泄露。在 js 中宰睡,函數(shù)即閉包,只有函數(shù)才會(huì)產(chǎn)生作用域的概念

閉包有三個(gè)特性:

  • 函數(shù)嵌套函數(shù)
  • 函數(shù)內(nèi)部可以引用外部的參數(shù)和變量
  • 參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收

請(qǐng)解釋一下 JavaScript 的同源策略

  • 概念:同源策略是客戶端腳本(尤其是 Javascript)的重要的安全度量標(biāo)準(zhǔn)气筋。它最早出自 Netscape Navigator2.0拆内,其目的是防止某個(gè)文檔或腳本從多個(gè)不同源裝載。這里的同源策略指的是:協(xié)議宠默,域名麸恍,端口相同,同源策略是一種安全協(xié)議
  • 指一段腳本只能讀取來(lái)自同一來(lái)源的窗口和文檔的屬性

為什么要有同源限制?

我們舉例說(shuō)明:比如一個(gè)黑客程序抹沪,他利用 Iframe 把真正的銀行登錄頁(yè)面嵌到他的頁(yè)面上刻肄,當(dāng)你使用真實(shí)的用戶名,密碼登錄時(shí)融欧,他的頁(yè)面就可以通過(guò) Javascript 讀取到你的表單中 input 中的內(nèi)容敏弃,這樣用戶名,密碼就輕松到手了噪馏。]

缺點(diǎn): 現(xiàn)在網(wǎng)站的 JS 都會(huì)進(jìn)行壓縮麦到,一些文件用了嚴(yán)格模式,而另一些沒(méi)有欠肾。這時(shí)這些本來(lái)是嚴(yán)格模式的文件瓶颠,被 merge 后,這個(gè)串就到了文件的中間刺桃,不僅沒(méi)有指示嚴(yán)格模式粹淋,反而在壓縮后浪費(fèi)了字節(jié)

實(shí)現(xiàn)一個(gè)函數(shù) clone,可以對(duì) JavaScript 中的 5 種主要的數(shù)據(jù)類(lèi)型(包括 Number瑟慈、String桃移、Object、Array封豪、Boolean)進(jìn)行值復(fù)制(城绰郑考)

function deepClone(obj) {
    if (!isObject(obj)) {
        throw new Error('obj 不是一個(gè)對(duì)象!')
    }

    let isArray = Array.isArray(obj)
    let cloneObj = isArray ? [] : {}
    for (let key in obj) {
        cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
    }

    return cloneObj
}

注意:for...in 法不支持拷貝 func吹埠、date第步、reg 和 err

// 代理法
function deepClone(obj) {
    if (!isObject(obj)) {
        throw new Error('obj 不是一個(gè)對(duì)象!')
    }

    let isArray = Array.isArray(obj)
    let cloneObj = isArray ? [...obj] : { ...obj }
    Reflect.ownKeys(cloneObj).forEach(key => {
        cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
    })

    return cloneObj
}

說(shuō)說(shuō)嚴(yán)格模式的限制

  • 嚴(yán)格模式主要有以下限制:
  • 變量必須聲明后再使用
  • 函數(shù)的參數(shù)不能有同名屬性缘琅,否則報(bào)錯(cuò)
  • 不能使用 with 語(yǔ)句
  • 不能對(duì)只讀屬性賦值粘都,否則報(bào)錯(cuò)
  • 不能使用前綴 0 表示八進(jìn)制數(shù),否則報(bào)錯(cuò)
  • 不能刪除不可刪除的屬性刷袍,否則報(bào)錯(cuò)
  • 不能刪除變量 delete prop翩隧,會(huì)報(bào)錯(cuò),只能刪除屬性 delete global[prop]
  • eval 不會(huì)在它的外層作用域引入變量
  • eval 和 arguments 不能被重新賦值
  • arguments 不會(huì)自動(dòng)反映函數(shù)參數(shù)的變化
  • 不能使用 arguments.callee
  • 不能使用 arguments.caller
  • 禁止 this 指向全局對(duì)象
  • 不能使用 fn.caller 和 fn.arguments 獲取函數(shù)調(diào)用的堆棧
  • 增加了保留字(比如 protected呻纹、static 和 interface)

如何刪除一個(gè) cookie

將時(shí)間設(shè)為當(dāng)前時(shí)間往前一點(diǎn)

var date = new Date();
date.setDate(date.getDate() - 1);//真正的刪除

setDate()方法用于設(shè)置一個(gè)月的某一天

expires 的設(shè)置

  document.cookie = 'user='+ encodeURIComponent('name')  + ';expires = ' + new Date(0)

編寫(xiě)一個(gè)方法 求一個(gè)字符串的字節(jié)長(zhǎng)度

假設(shè):一個(gè)英文字符占用一個(gè)字節(jié)堆生,一個(gè)中文字符占用兩個(gè)字節(jié)

function GetBytes(str){

        var len = str.length;

        var bytes = len;

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

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

        }

        return bytes;

    }

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

請(qǐng)解釋什么是事件代理

事件代理(Event Delegation),又稱之為事件委托雷酪。是 JavaScript 中常用綁定事件的常用技巧淑仆。顧名思義,“事件代理”即是把原本需要綁定的事件委托給父元素哥力,讓父元素?fù)?dān)當(dāng)事件監(jiān)聽(tīng)的職務(wù)蔗怠。事件代理的原理是 DOM 元素的事件冒泡墩弯。使用事件代理的好處是可以提高性能

attribute 和 property 的區(qū)別是什么?

  • attribute 是 dom 元素在文檔中作為 html 標(biāo)簽擁有的屬性寞射;
  • property 就是 dom 元素在 js 中作為對(duì)象擁有的屬性渔工。
  • 對(duì)于 html 的標(biāo)準(zhǔn)屬性來(lái)說(shuō),attribute 和 property 是同步的桥温,是會(huì)自動(dòng)更新的
  • 但是對(duì)于自定義的屬性來(lái)說(shuō)引矩,他們是不同步的

頁(yè)面編碼和被請(qǐng)求的資源編碼如果不一致如何處理?

  • 后端響應(yīng)頭設(shè)置 charset
  • 前端頁(yè)面<meta>設(shè)置 charset

<script> 放在 </body> 之前和之后有什么區(qū)別策治?瀏覽器會(huì)如何解析它們脓魏?

按照 HTML 標(biāo)準(zhǔn),在</body>結(jié)束后出現(xiàn)<script>或任何元素的開(kāi)始標(biāo)簽通惫,都是解析錯(cuò)誤
雖然不符合 HTML 標(biāo)準(zhǔn)茂翔,但瀏覽器會(huì)自動(dòng)容錯(cuò),使實(shí)際效果與寫(xiě)在</body>之前沒(méi)有區(qū)別
瀏覽器的容錯(cuò)機(jī)制會(huì)忽略<script>之前的</body>履腋,視作<script>仍在 body 體內(nèi)珊燎。省略</body></html>閉合標(biāo)簽符合 HTML 標(biāo)準(zhǔn),服務(wù)器可以利用這一標(biāo)準(zhǔn)

<script> 放在 </head> 中會(huì)有什么問(wèn)題遵湖?

在瀏覽器渲染頁(yè)面之前悔政,它需要通過(guò)解析HTML標(biāo)記然后構(gòu)建DOM樹(shù)。在這個(gè)過(guò)程中延旧,如果解析器遇到了一個(gè)腳本(script)谋国,它就會(huì)停下來(lái),并且執(zhí)行這個(gè)腳本迁沫,然后才會(huì)繼續(xù)解析HTML芦瘾。如果遇到了一個(gè)引用外部資源的腳本(script),它就必須停下來(lái)等待這個(gè)腳本資源的下載集畅,而這個(gè)行為會(huì)導(dǎo)致一個(gè)或者多個(gè)的網(wǎng)絡(luò)往返近弟,并且會(huì)延遲頁(yè)面的首次渲染時(shí)間。

還有一點(diǎn)是需要我們注意的挺智,那就是外部引入的腳本(script)會(huì)阻塞瀏覽器的并行下載祷愉,HTTP/1.1規(guī)范表明,瀏覽器在每個(gè)主機(jī)下并行下載的組件不超過(guò)兩個(gè)(也就是說(shuō)赦颇,瀏覽器一次只能夠同時(shí)從同一個(gè)服務(wù)器加載兩個(gè)腳本)二鳄;如果你網(wǎng)站的圖片是通過(guò)多個(gè)服務(wù)器提供的,那么按道理來(lái)說(shuō)媒怯,你的網(wǎng)站可以一次并行下載多張圖片订讼。但是,當(dāng)我們網(wǎng)站在加載腳本的時(shí)候沪摄;瀏覽器不會(huì)再啟動(dòng)任何其它的下載躯嫉,即使這些組件來(lái)自不同的服務(wù)器。

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

  • 設(shè)置<script>屬性 async="async" (一旦腳本可用祈餐,則會(huì)異步執(zhí)行)
  • 動(dòng)態(tài)創(chuàng)建 script DOM:document.createElement('script');
  • XmlHttpRequest 腳本注入
  • 異步加載庫(kù) LABjs
  • 模塊加載器 Sea.js

JavaScript 中,調(diào)用函數(shù)有哪幾種方式哄陶?

  • 方法調(diào)用模式 Foo.foo(arg1, arg2);
  • 函數(shù)調(diào)用模式 foo(arg1, arg2);
  • 構(gòu)造器調(diào)用模式 (new Foo())(arg1, arg2);
  • call/applay 調(diào)用模式 Foo.foo.call(that, arg1, arg2);
  • bind 調(diào)用模式 Foo.foo.bind(that)(arg1, arg2)();

簡(jiǎn)單實(shí)現(xiàn) Function.bind 函數(shù)帆阳?

  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, 1));
      }
    }
  }
  // 只支持 bind 階段的默認(rèn)參數(shù):
  func.bind(that, arg1, arg2)();

  // 不支持以下調(diào)用階段傳入的參數(shù):
  func.bind(that)(arg1, arg2);

列舉一下 JavaScript 數(shù)組和對(duì)象有哪些原生方法

  • 數(shù)組:
    • 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
  • 對(duì)象:
    • object.hasOwnProperty(prop);
    • object.propertyIsEnumerable(prop);
    • object.valueOf();
    • object.toString();
    • object.toLocaleString();
    • Class.prototype.isPropertyOf(object);

Array.slice() 與 Array.splice() 的區(qū)別?

  • slice -- “讀取”數(shù)組指定的元素屋吨,不會(huì)對(duì)原數(shù)組進(jìn)行修改

    • 語(yǔ)法:arr.slice(start, end)
    • start 指定選取開(kāi)始位置(含)
    • end 指定選取結(jié)束位置(不含)
  • splice

    • “操作”數(shù)組指定的元素蜒谤,會(huì)修改原數(shù)組,返回被刪除的元素
    • 語(yǔ)法:arr.splice(index, count, [insert Elements])
    • index 是操作的起始位置
    • count = 0 插入元素至扰,count > 0 刪除元素
    • [insert Elements] 向數(shù)組新插入的元素

JavaScript 對(duì)象生命周期的理解鳍徽?

  • 當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),JavaScript 會(huì)自動(dòng)為該對(duì)象分配適當(dāng)?shù)膬?nèi)存
  • 垃圾回收器定期掃描對(duì)象敢课,并計(jì)算引用了該對(duì)象的其他對(duì)象的數(shù)量
  • 如果被引用數(shù)量為 0阶祭,或惟一引用是循環(huán)的,那么該對(duì)象的內(nèi)存即可回收

哪些操作會(huì)造成內(nèi)存泄漏直秆?

  • JavaScript 內(nèi)存泄露指對(duì)象在不需要使用它時(shí)仍然存在濒募,導(dǎo)致占用的內(nèi)存不能使用或回收
  • 未使用 var 聲明的全局變量
  • 閉包函數(shù)(Closures)
  • 循環(huán)引用(兩個(gè)對(duì)象相互引用)
  • 控制臺(tái)日志(console.log)
  • 移除存在綁定事件的 DOM 元素(IE)

在 javascript 中,1 與 Number(1)有什么區(qū)別 [易混淆]

var a = Number(1) // 1
var b = new Number(1)  // Number {[[PrimitiveValue]]: 1}
typeof (a) // number
typeof (b) // object
a == b // true
  • var a = 1 是一個(gè)常量圾结,而 Number(1)是一個(gè)函數(shù)
  • new Number(1)返回的是一個(gè)對(duì)象
  • a==b 為 true 是因?yàn)樗栽谇笾颠^(guò)程中瑰剃,總是會(huì)強(qiáng)制轉(zhuǎn)為原始數(shù)據(jù)類(lèi)型而非對(duì)象,例如下面的代碼:
typeof 123 // "number"
typeof new Number(123) // "object"
123 instanceof Number // false
(new Number(123)) instanceof Number // true
123 === new Number(123) // false

參考地址:面試題:在 javascript 中筝野,1 與 Number(1)有什么區(qū)別

console.log(!!(new Boolean(false))輸出什么 [易混淆]

true

布爾的包裝對(duì)象 Boolean 的對(duì)象實(shí)例晌姚,對(duì)象只有在 null 與 undefined 時(shí),才會(huì)認(rèn)定為布爾的 false 值遗座,布爾包裝對(duì)象本身是個(gè)對(duì)象舀凛,對(duì)象->布爾 都是 true,所以 new Boolean(false)其實(shí)是布爾的 true途蒋,看下面這段代碼:

if(new Boolean(false)){
    alert('true!!');
}

只有使用了 valueOf 后才是真正的轉(zhuǎn)換布爾值猛遍,與上面包裝對(duì)象與原始資料轉(zhuǎn)換說(shuō)明的相同:

!!(new Boolean(false))  //true
(new Boolean(false)).valueOf() //false

為什么 JS 是單線程,而不是多線程 [常考]

  • 單線程是指 JavaScript 在執(zhí)行的時(shí)候号坡,有且只有一個(gè)主線程來(lái)處理所有的任務(wù)懊烤。
  • 目的是為了實(shí)現(xiàn)與瀏覽器交互。
  • 我們?cè)O(shè)想一下宽堆,如果 JavaScript 是多線程的腌紧,現(xiàn)在我們?cè)跒g覽器中同時(shí)操作一個(gè) DOM,一個(gè)線程要求瀏覽器在這個(gè) DOM 中添加節(jié)點(diǎn)畜隶,而另一個(gè)線程卻要求瀏覽器刪掉這個(gè) DOM 節(jié)點(diǎn)壁肋,那這個(gè)時(shí)候?yàn)g覽器就會(huì)很郁悶号胚,他不知道應(yīng)該以哪個(gè)線程為準(zhǔn)。所以為了避免此類(lèi)現(xiàn)象的發(fā)生浸遗,降低復(fù)雜度猫胁,JavaScript 選擇只用一個(gè)主線程來(lái)執(zhí)行代碼,以此來(lái)保證程序執(zhí)行的一致性跛锌。

瀏覽器中的 Event Loop

[圖片上傳失敗...(image-fd50b0-1620536284225)]

  • 主線程運(yùn)行的時(shí)候會(huì)生成堆(heap)和棧(stack)弃秆;
  • js 從上到下解析方法,將其中的同步任務(wù)按照?qǐng)?zhí)行順序排列到執(zhí)行棧中髓帽;
  • 當(dāng)程序調(diào)用外部的 API 時(shí)菠赚,比如 ajax、setTimeout 等郑藏,會(huì)將此類(lèi)異步任務(wù)掛起衡查,繼續(xù)執(zhí)行執(zhí)行棧中的任務(wù),等異步任務(wù)返回結(jié)果后必盖,再按照?qǐng)?zhí)行順序排列到事件隊(duì)列中峡捡;
  • 主線程先將執(zhí)行棧中的同步任務(wù)清空,然后檢查事件隊(duì)列中是否有任務(wù)筑悴,如果有们拙,就將第一個(gè)事件對(duì)應(yīng)的回調(diào)推到執(zhí)行棧中執(zhí)行,若在執(zhí)行過(guò)程中遇到異步任務(wù)阁吝,則繼續(xù)將這個(gè)異步任務(wù)排列到事件隊(duì)列中砚婆。
  • 主線程每次將執(zhí)行棧清空后,就去事件隊(duì)列中檢查是否有任務(wù)突勇,如果有装盯,就每次取出一個(gè)推到執(zhí)行棧中執(zhí)行,這個(gè)過(guò)程是循環(huán)往復(fù)的... ...甲馋,這個(gè)過(guò)程被稱為“Event Loop 事件循環(huán)”

參考地址:Event Loop 這個(gè)循環(huán)你曉得么埂奈?(附 GIF 詳解)-餓了么前端

給Object擴(kuò)展方法clone,實(shí)現(xiàn)對(duì)象的深拷貝

Object.prototype.clone = function () {
  //判斷拷貝的要進(jìn)行深拷貝的是數(shù)組還是對(duì)象定躏,是數(shù)組的話進(jìn)行數(shù)組拷貝账磺,對(duì)象的話進(jìn)行對(duì)象拷貝
  var objClone = Array.isArray(obj) ? [] : {};
  //進(jìn)行深拷貝的不能為空,并且是對(duì)象或者是
  if (obj && typeof obj === "object") {
    for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      if (obj[key] && typeof obj[key] === "object") {
        objClone[key] = deepClone1(obj[key]);
      } else {
        objClone[key] = obj[key];
      }
    }
    }
  }
  return objClone;
}

實(shí)現(xiàn)一個(gè)querySearch函數(shù)痊远,可以傳入一個(gè)參數(shù)垮抗,得到對(duì)應(yīng)url中的value值

function getQueryString(name) {
    var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
    var r = window.location.search.substr(1).match(reg);
    if (r != null) {
        return unescape(r[2]);
    }
    return null;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碧聪,隨后出現(xiàn)的幾起案子冒版,更是在濱河造成了極大的恐慌,老刑警劉巖逞姿,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辞嗡,死亡現(xiàn)場(chǎng)離奇詭異捆等,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)续室,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)楚里,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人猎贴,你說(shuō)我怎么就攤上這事『猓” “怎么了她渴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蔑祟。 經(jīng)常有香客問(wèn)我趁耗,道長(zhǎng),這世上最難降的妖魔是什么疆虚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任苛败,我火速辦了婚禮,結(jié)果婚禮上径簿,老公的妹妹穿的比我還像新娘罢屈。我一直安慰自己,他們只是感情好篇亭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布缠捌。 她就那樣靜靜地躺著,像睡著了一般译蒂。 火紅的嫁衣襯著肌膚如雪曼月。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天柔昼,我揣著相機(jī)與錄音哑芹,去河邊找鬼。 笑死捕透,一個(gè)胖子當(dāng)著我的面吹牛聪姿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乙嘀,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼咳燕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了乒躺?” 一聲冷哼從身側(cè)響起招盲,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嘉冒,沒(méi)想到半個(gè)月后曹货,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體咆繁,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年顶籽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玩般。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡礼饱,死狀恐怖坏为,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情镊绪,我是刑警寧澤匀伏,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蝴韭,受9級(jí)特大地震影響够颠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榄鉴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一履磨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庆尘,春花似錦剃诅、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至位岔,卻和暖如春如筛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抒抬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工杨刨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人擦剑。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓妖胀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親惠勒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赚抡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容