前端面試——JavaScript

嚴格相等 ===

全等操做符比較兩個值是否相等,兩個被比較的值在比較前都不進行隱式轉換。若是兩個被比較的值具備不一樣的類型,這兩個值是不全等的。不然,若是兩個被比較的值類型相同,值也相同,而且都不是 number 類型時,兩個值全等。最後,若是兩個值都是 number 類型,當兩個都不是 NaN,而且數值相同,或是兩個值分別爲 +0 和 -0 時,兩個值被認爲是全等的。javascript

非嚴格相等 ==

相等操做符比較兩個值是否相等,在比較前將兩個被比較的值轉換爲相同類型。在轉換後(等式的一邊或兩邊均可能被轉換),最終的比較方式等同於全等操做符 === 的比較方式。 相等操做符知足交換律。java

同值相等由 Object.is 方法提供

Object.is =function(x,y){
    if(x === y){
        // +0,-0狀況處理
        return x !==0 || 1/x ===1/y;
    }else{
        //NaN
        return x !== x && y!==y;
    }
}複製代碼

typeof一元運算符

typeof只有一個實際應用場景,就是用來檢測一個對象是否已經定義或者是否已經賦值。node

instanceof

instanceof運算符能夠用來判斷某個構造函數的prototype屬性是否存在於另一個要檢測對象的原型鏈上。 nginx

function _instanceOf(left,right){
    let prototype =right.prototype;
    obj = left.__proto__;
    while(true){
        if(obj === null) return false;
        if(obj === prototype) return true;
        obj = obj.__proto__;
    }
}
function Person(){
    this.name = "first";
}
let person = new Person();
console.log(_instanceOf(person,Person));複製代碼

Object.prototype.toString.call()

Object.prototype.toString.call() 經常使用於判斷瀏覽器內置對象時。
web

const an = ['Hello','An'];
an.toString(); // "Hello,An"
Object.prototype.toString.call(an); // "[object Array]"
Object.prototype.toString.call('An') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call({name: 'An'}) // "[object Object]"複製代碼

Array.isArray()

  • 功能:用來判斷對象是否爲數組算法

  • instanceof 與 isArray 當檢測Array實例時,Array.isArray 優於 instanceof ,由於 Array.isArray 能夠檢測出 iframesjson

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]
// Correctly checking for Array
Array.isArray(arr);  // true
Object.prototype.toString.call(arr); // true
// Considered harmful, because doesn't work though iframes arr instanceof Array; // false 複製代碼

原始類型有幾種?

在 JS 中,存在着 6 種原始值,分別是:跨域

一、boolean 二、null 三、undefined 四、number 五、string 六、symbol數組

首先原始類型存儲的都是值,是沒有函數能夠調用的。
瀏覽器

爲何typeof null 是 object?

在 JS 的最第一版本中使用的是 32 位系統,爲了性能考慮使用低位存儲變量的類型信息,000 開頭表明是對象,然而 null 表示爲全零,因此將它錯誤的判斷爲 object 。雖然如今的內部類型判斷代碼已經改變了,可是對於這個 Bug 倒是一直流傳下來。

對象類型和原始類型的不一樣之處?函數參數是對象會發生什麼問題?

對象類型和原始類型不一樣的是,原始類型存儲的是值,對象類型存儲的是地址(指針)。當你建立了一個對象類型的時候,計算機會在內存中幫咱們開闢一個空間來存放值,可是咱們須要找到這個空間,這個空間會擁有一個地址(指針)。

Js中建立對象的幾種方式

1.普通方法

1)字面量的方式

var box={
    name:'蘇',
    age:22,
    run:function(){
        return this.name+this.age
    }
}複製代碼

2)new 方法:

var box = new object();
box.name = '蘇';
box.age =22;
//缺點:想建立相似的對象,即屬性,方法名相同可是值不相同的對象,就須要寫不少代碼。複製代碼

  1. 建立一個新對象。

  2. 這個新對象會被執行[[原型]]鏈接。

  3. 屬性和方法被加入到 this 引用的對象中。並執行了構造函數中的方法.

  4. 若是函數沒有返回其餘對象,那麼this指向這個新對象,不然this指向構造函數中返回的對象。

function new(func){
    let target ={};
    target.__proto__ = func.prototype;
    let res = func.call(target);
    if(res &&typeof(res) =="object" || typeof(res) =="function"){
        return res;
    }
    return target;
}複製代碼

2.工廠模式

function createObjet(name ,age){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.run = function (){
         return this.name + this.age;
    }
    return obj;
}
var a = createObjet('lao',22);
//缺點:沒有辦法判斷對象的來源,即沒法判斷出對象是誰建立出來的實例。存在識別問題。複製代碼

3.構造函數式

function Box(name,age){
    this.name = name;
    this.age = age;
    this.run = function(){
        return this.name + this.age;
    }
}
var a = new Box('lao',22);複製代碼

優勢:解決了識別問題,即建立每一個實例對象都是這個Box的實例。

缺點:經過構造函數建立對象的時候,當一個構造函數被實例化屢次後,構造函數中的方法也會被實例化屢次,同一類型的不一樣對象之間並不共用同一個函數,形成內存浪費。

var a = new Box('11',22);
var b = new Box('11',22);
a.run() == b.run();//true;
a.run = b.run //false;引用地址不一樣複製代碼

4.原型鏈方式

function Box(){};
Box.prototype={
	constructor:Box,  //強制轉到Box
	name:'11',
	age:22,
	run:function(){
		return this.name+this.age;
	}
}複製代碼

缺點:沒有辦法傳參。可在實例裏來添加或覆蓋原型屬性:

特色:原型裏的屬性和方法都是共享的。

5.構造函數加原型模式

function Box(name,age){
	this.name=name;
	this.age=age;
}
Box.prototype.run=function(){
	return this.name+this.age;
}複製代碼

方式:須要傳參的實例屬性用構造函數,須要共享的方法用原型模式。

缺點:無論是否調用了原型裏的方法,在建立實例對象時,都會對方法進行初始化。

深淺拷貝

淺拷貝

淺拷貝只會將對象的各個屬性進行依次複製,並不會進行遞歸複製,也就是說只會賦值目標對象的第一層屬性。 對於目標對象第一層爲基本數據類型的數據,就是直接賦值,即「傳值」; 而對於目標對象第一層爲引用數據類型的數據,就是直接賦存於棧內存中的堆內存地址,即「傳址」。

深拷貝

深拷貝不一樣於淺拷貝,它不僅拷貝目標對象的第一層屬性,而是遞歸拷貝目標對象的全部屬性。 ​ 通常來講,在JavaScript中考慮複合類型的深層複製的時候,每每就是指對於 Date 、Object 與 Array 這三個複合類型的處理。

一、經過 JSON.parse(JSON.stringify(object)) 來解決

  • 會忽略 undefined

  • 會忽略 symbol

  • 不能序列化函數

  • 不能解決循環引用的對象

二、遞歸實現

function deepCopy(obj1) {
      var obj2 = Array.isArray(obj1) ? [] : {};
      if (obj1 && typeof obj1 === "object") {
        for (var i in obj1) {
          if (obj1.hasOwnProperty(i)) {
            // 若是子屬性爲引用數據類型,遞歸複製
            if (obj1[i] && typeof obj1[i] === "object") {
              obj2[i] = deepCopy(obj1[i]);
            } else {
              // 若是是基本數據類型,只是簡單的複製
              obj2[i] = obj1[i];
            }
          }
        }
      }
      return obj2;
    }
    var obj1 = {
      a: 1,
      b: 2,
      c: {
        d: 3
      }
    }
    var obj2 = deepCopy(obj1);
    obj2.a = 3;
    obj2.c.d = 4;
    alert(obj1.a); // 1
    alert(obj2.a); // 3
    alert(obj1.c.d); // 3
    alert(obj2.c.d); // 4複製代碼

JS閉包的實現以及缺陷

閉包就是指有權訪問另外一個函數做用域中的變量的函數。

function  func(){
	var a=1,b=2;
	function closure(){
		return a+b;
	}
	return closure;
}複製代碼

一般,函數的做用域及其全部變量都會在函數執行結束後被銷燬。可是,在建立了一個閉包之後,這個函數的做用域就會一直保存到閉包不存在爲止。 在javascript中,若是一個對象再也不被引用,那麼這個對象就會被垃圾回收機制回收;

閉包只能取得包含函數中任何變量的最後一個值,這是由於閉包所保存的是整個變量對象,而不是某個特殊的變量。

obj.getName()()其實是在全局做用域中調用了匿名函數,this指向了window。這裏要理解函數名與函數功能(或者稱函數值)是分割開的,不要認爲函數在哪裏,其內部的this就指向哪裏。匿名函數的執行環境具備全局性,所以其 this 對象一般指向 window。

var name = "The Window";
var obj = {
    name: "My Object",
    getName:function(){
        return function(){
            return  this.name;
        };
    }
};
console.log(obj.getName()());複製代碼

閉包的應用

一、應用閉包的主要場合是:設計私有的方法和變量。

二、模塊模式:爲單例建立私有變量和方法。

匿名函數最大的用途是建立閉包,而且還能夠構建命名空間,以減小全局變量的使用。從而使用閉包模塊化代碼,減小全局變量的污染。

三、函數做爲返回值

四、函數做爲參數傳遞

閉包的缺陷

  • 閉包的缺點就是常駐內存會增大內存使用量,而且使用不當很容易形成內存泄露。

  • 若是不是由於某些特殊任務而須要閉包,在沒有必要的狀況下,在其它函數中建立函數是不明智的,由於閉包對腳本性能具備負面影響,包括處理速度和內存消耗。

內存泄漏

內存泄漏指因爲疏忽或錯誤形成程序未能釋放已經再也不使用的內存。內存泄漏並不是指內存在物理上的消失,而是應用程序分配某段內存後,因爲設計錯誤,致使在釋放該段內存以前就失去了對該段內存的控制,從而形成了內存的浪費。

1.console.log()

2.閉包

3.意外的全局變量

函數柯里化

柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。

一、參數複用

function check(reg,txt){
    return reg.test(txt);
}
check(/\d+/g,'test'); //false
check*(/[a-z]+/g,'test'); //true

function curryingCheck(reg){
    return function(txt){
        return reg.test(txt);
    }
}
var hasNumber = curryingCheck(/\d+/g);
var hasLetter = curryingCheck(/[a-z]+/g);

hasNumber('test1');//true
hasNumber('testtest');//false
hasLetter('212222')//flase複製代碼

二、延遲運行

function.prototype.bing = function(context){
    var _this = this;
    var args = Array.prototype.slice.call(arguments,1)
return function(){
    return _this.apply(context,args);
  }
}複製代碼

三、常見考題

function add(){
    //第一次執行時,定義一個數組專門用來存儲全部的參數
    var _args = Array.prototype.slice.call(arguments);
    //在內部聲明一個函數,利用閉包的特性保存_args並收集全部的參數值。
    var _adder = function(){
        _args.push(...arguments);
        return _adder;
    };
    //利用toString隱式轉換的特性,當最後執行隱式轉換,並計算最終返回的值
    _adder.toString = function(){
        return _args.reduce(function(a,b){
            return a+b;
        });
    }
    return _adder;
}

add(1)(2)(3);
add(1,2,3)(4);
add(1)(2)(3)(4)(5);複製代碼

 JS垃圾回收機制

新生代算法

在新生代空間中,內存空間分爲兩部分,分別爲 From 空間和 To 空間。在這兩個空間中,一定有一個空間是使用的,另外一個空間是空閒的。新分配的對象會被放入 From 空間中,當 From 空間被佔滿時,新生代 GC 就會啓動了。算法會檢查 From 空間中存活的對象並複製到 To 空間中,若是有失活的對象就會銷燬。當複製完成後將 From 空間和 To 空間互換,這樣 GC 就結束了。

老生代算法

老生代中的對象通常存活時間較長且數量也多,使用了兩個算法,分別是標記清除算法和標記壓縮算法。

一、新生代中的對象是否已經經歷過一次 Scavenge 算法,若是經歷過的話,會將對象重新生代空間移到老生代空間中。 二、To 空間的對象佔比大小超過 25 %。在這種狀況下,爲了避免影響到內存分配,會將對象重新生代空間移到老生代空間中。 老生代中的空間很複雜,有以下幾個空間 在老生代中,如下狀況會先啓動標記清除算法: (1)某一個空間沒有分塊的時候 (2)空間中被對象超過必定限制 (3)空間不能保證新生代中的對象移動到老生代中 在這個階段中,會遍歷堆中全部的對象,而後標記活的對象,在標記完成後,銷燬全部沒有被標記的對象。在標記大型對內存時,可能須要幾百毫秒才能完成一次標記。這就會致使一些性能上的問題。 ​ 清除對象後會形成堆內存出現碎片的狀況,當碎片超過必定限制後會啓動壓縮算法。在壓縮過程當中,將活的對象像一端移動,直到全部對象都移動完成而後清理掉不須要的內存。

如何改變this指向?

原則:this 永遠指向最後調用它的那個對象

一、使用 ES6 的箭頭函數

箭頭函數的 this 始終指向函數定義時的 this,而非執行時。箭頭函數須要記着這句話:「箭頭函數中沒有 this 綁定,必須經過查找做用域鏈來決定其值若是箭頭函數被非箭頭函數包含,則 this 綁定的是最近一層非箭頭函數的 this,不然,this 爲 undefined」。

二、在函數內部使用 _this = this

三、使用 apply、call、bind

apply傳遞數組,call傳遞參數,而bind改變this指向的時候返回的是一個函數。而apply、call是當即執行。

call模擬實現

Function.prototype.call =function(context){
    context = context? Object(context): window;
    context.fn = this;//this也就是調用call的函數
    let args = [...argments].slice(1);
    let r = context.fn(...args);
    delete context.fn;
    return r;
}複製代碼

apply模擬實現

Function.prototype.apply = function(context,args){
        context = context? Object(context): window;//不傳遞context默認爲window
        context.fn = this;
        if(!args){
            return context.fn();
        }
        let r = context.fn(...args);
        delete context.fn;
        return r;
}複製代碼

bind模擬實現

Function.prototype.bind = function(context){
    const _this = this;
    const argus = Array.prototype.slice.apply(argments,[1]) //拿到除了context以外的預置參數序列
    return function(){
        _this.apply(context,argus.concat(Array.prototype.slice.call(argments)))
        //綁定this同時將調用時傳遞的序列和預置序列進行合併
    }
}複製代碼

四、new 實例化一個對象

手寫flat()

//方法一

    function ArrayFlat(arr){
    let a = [];
    arr.forEach(item){
        if(Array.isArray(item)){
            ArrayFlat(item);
        }else{
            a.push(item);
        }
    }
    return a;
}

//方法二

var a = arr.toString.split(',');
for(var i=0;i<a.length;i++){
    a[i] =eval(arr[i]);
}複製代碼

去重

let arr =[1,2,3,22,233,22,2,233,'a',3,'b','a'];
Array.prototype.unique = function(){
    const newArray = [];
    this.forEach(item =>{
        if(newArray.indexOf(item)===-1){
            newArray.push(item);
        }
    });
    return newArray;
}複製代碼

Array.prototype.unique = function(){
    return [...new Set(this)];
}複製代碼

Array.prototype.unique =function(){
    const newArray=[];
    let isRepeat;
    for(let i=0;i<this.length;i++){
        isRepeat =false;
        for(let j=i+1;i<this.length;j++){
            if(this[i]===this[j]){
                isRepeat = true;
                break;
            }
        }
        if(!isRepeat){
            newArray.push(this[i]);
        }
    }
    return newArray;
}複製代碼

實現一個 reduce 函數

function reduce(param,callback ,initVal){
    var hasInitVal = initVal !== void 0;
    var acc =hasInitVal ? initVal: param[0];
    each(hasInitVal? param:Array.prototype.slice.call(param,1),function(v,k,o){
        acc = callback(acc,v,k,o);
    });
    return acc;
}複製代碼

爲數組添加Math方法

// ES5 的寫法 
Math.max.apply(null, [14, 3, 77, 30]);
 // ES6 的寫法 
Math.max(...[14, 3, 77, 30]); 
// reduce 
[14,3,77,30].reduce((accumulator, currentValue)=>{
     return accumulator = accumulator > currentValue ? accumulator : currentValue 
});複製代碼

JS獲取對象的key值的方式,for in,for of,foreach,map,reduce,fliter

一、for...of循環:

具備 iterator 接口,就能夠用for...of循環遍歷它的成員(屬性值)。for...of循環可使用的範圍包括數組、Set 和 Map 結構、某些相似數組的對象、Generator 對象,以及字符串。

function* generator(){ 
  yield 1; 
  yield 2; 
  yield 3; 
}; 

for (const g of generator()) { 
  console.log(g); 
}複製代碼

二、for...in循環:

遍歷對象自身的和繼承的可枚舉的屬性, 不能直接獲取屬性值。能夠中斷循環。

三、forEach:

只能遍歷數組,不能中斷,沒有返回值(或認爲返回值是undefined)。

[].forEach(function(value, index, array) {
  // ...
});複製代碼

四、map:

只能遍歷數組,不能中斷,返回值是修改後的數組。

[].map(function(value, index, array) {
  // ...
});複製代碼

五、filter:

var ages = [32, 33, 16, 40];

function checkAdult(age) {
    return age >= 18;
}

function myFunction() {
    document.getElementById("demo").innerHTML = ages.filter(checkAdult);
}複製代碼

六、Some

var scores = [5,8,3,10];
var current = 7;

function higherThanCurrent(score){
    return score > current;
}
if(scores.some(higherThanCurrent)){
    alert(1);
}複製代碼

七、every

if(scroes.every(higherThanCurrent)){
    console.log(1);
}else{
    console.log(2);
}複製代碼

八、reduce

var sum = [1, 2, 3, 4].reduce(function (previous, current, index, array) {
  return previous + current;
});
console.log(sum); // 10複製代碼

原型、構造函數、實例、原型鏈的關係如何?


  1. javascript中的繼承是經過原型鏈來體現的。
  2. 每一個對象都有一個__proto__屬性,指向構造函數的prototype。
  3. 訪問一個對象的屬性時,先在基本屬性中查找,若是沒有,再沿着__proto__這條鏈向上找,這就是原型鏈。

JS繼承

一、原型鏈繼承
function Parent(name){
    this.name = name;
}
function Child(name){
    this.name = name;
}
Parent.prototype.sayName = function(){
    console.log('parent name:',this.name);
}
Child.protoType = new Parent('father');
Child.protoType.constructor = Child;
Child.protoType.sayName = function(){
    console.log('child name:',this.name);
}
var child = new Child('son');
child.sayName();
//此種繼承的缺點:子類沒法給父類傳遞參數,而且Child.protoType 要寫在new Parent後面


二、構造函數繼承
function Parent(name){
    this.name = name;
}
Parent.protoType.sayName = function(){
    console.log('parent name:',this.name);
}
Parent.protoType.doSomeThing = function(){
    console.log('parent doSomeThing');
}
function Child(name,fatherName){
    Parent.call(this,faterName);
    this.name = name;
}
Child.protoType.sayNmae =  function(){
    console.log('child name:',this.name);
}
var child = new Child('son','father');
child.sayName();
child.doSomeThing();
//此方案的缺點:沒有原型,每次建立一個Child實例對象的時候都要執行一遍Parent函數


三、組合式繼承
function Parent(name){
    this.name = name;
}
Parent.protoType.sayName = function(){
    console.log('parent name:',this.name);
}
Parent.protoType.doSomeThing = function(){
    console.log('parent doSomeThing');
}
function Child(name,fatherName){
    Parent.call(this,faterName);
    this.name = name;
}
Child.protoType = Parent.protoType;
Child.protoType.constructor = Child;
Child.protoType.sayNmae =  function(){
    console.log('child name:',this.name);
}
var child = new Child('son');
child.sayName();
child.doSomeThing();
//經過原型鏈實現對象屬性和方法的繼承,而經過構造函數來實現實例屬性的繼承。


四、寄生組合繼承
function Parent(name){
    this.name = name;
}
Parent.protoType.sayName = function(){
    console.log('parent name:',this.name);
}
Parent.protoType.doSomeThing = function(){
    console.log('parent doSomeThing');
}
function Child(name,fatherName){
    Parent.call(this,faterName);
    this.name = name;
}
function create(proto){
    function F(){}
    F.proto=proto;
    return new F();
}
Child.protoType = create(Parent.protoType);
Child.protoType.sayNmae =  function(){
    console.log('child name:',this.name);
}
Child.protoType.constructor = Child;
//用F(){}去替代父類執行構造函數

五、ES6繼承
Class Parent{
    constructor(name){
        this.name = name;
    }
    doSomething(){
        console.log('parent do sometthing!');
    }
    sayName(){
        console.log('parent name:',this.name);
    }
}
class Child extend Parent{
    constructor(name,parentName){
        super(parentName);
        this.name =name;
    }
    sayName(){
        console.log('child name:',this.name);
    }
}
//ES6繼承 複製代碼

跨域

同源策略:端口、域名、協議

1.jsonp

JSONP優勢是簡單兼容性好,可用於解決主流瀏覽器的跨域數據訪問的問題。缺點是僅支持get方法具備侷限性,不安全可能會遭受XSS攻擊

(1)聲明一個回調函數,其函數名(如show)當作參數值,要傳遞給跨域請求數據的服務器,函數形參爲要獲取目標數據(服務器返回的data)。

(2)建立一個<script>標籤,把那個跨域的API數據接口地址,賦值給script的src,還要在這個地址中向服務器傳遞該函數名(能夠經過問號傳參:?callback=show)。

(3)服務器接收到請求後,須要進行特殊的處理:把傳遞進來的函數名和它須要給你的數據拼接成一個字符串,例如:傳遞進去的函數名是show,它準備好的數據是show('我不愛你')。

(4)最後服務器把準備的數據經過HTTP協議返回給客戶端,客戶端再調用執行以前聲明的回調函數(show),對返回的數據進行操做。 在開發中可能會遇到多個 JSONP 請求的回調函數名是相同的,這時候就須要本身封裝一個 JSONP函數。

2.cors

服務端設置 Access-Control-Allow-Origin 就能夠開啓 CORS。 該屬性表示哪些域名能夠訪問資源,若是設置通配符則表示全部網站均可以訪問資源。

3.postMessage

(1)頁面和其打開的新窗口的數據傳遞

(2)多窗口之間消息傳遞

(3)頁面與嵌套的iframe消息傳遞

四、Node中間件代理(兩次跨域)

實現原理:同源策略是瀏覽器須要遵循的標準,而若是是服務器向服務器請求就無需遵循同源策略。 代理服務器,須要作如下幾個步驟:

  • 接受客戶端請求 。

  • 將請求 轉發給服務器。

  • 拿到服務器 響應 數據。

  • 將 響應 轉發給客戶端。

五、nginx反向代理

實現原理相似於Node中間件代理,須要你搭建一箇中轉nginx服務器,用於轉發請求。

六、window.name + iframe

七、location.hash + iframe

八、document.domain + iframe

節流、防抖

防抖(debounce) 所謂防抖,就是指觸發事件後在 n 秒內函數只能執行一次,若是在 n 秒內又觸發了事件,則會從新計算函數執行時間。

function debounce(func,wait){
    let timeout;
    return function(){
        let context = this;
        let args = arguments;
        
    if(timeout) clearTimeout(timeout);
    timeout = setTimeout(()=>{
        func.apply(context,args)
    },wait);
	}
}複製代碼

節流(throttle) 所謂節流,就是指連續觸發事件可是在 n 秒中只執行一次函數。 節流會稀釋函數的執行頻率。

function throttle(func,wait){
    var previous = 0;
    return fucntion(){
        let now = Date.now();
        let context = this;
        let args = arguments;
        if(now -previous > wait){
            func.apply(context,args);
            previous = now;
        }
    }
}複製代碼

嚴格模式

一、在對象中聲明相同的屬性名

二、在函數聲明中相同的參數名

三、不能用前導0聲明8進制直接量

四、不能從新聲明、刪除或重寫eval和arguments這兩個標示符

五、用delete刪除顯示聲明的標識符、名稱和具名函數

六、代碼中使用擴展的保留字,例如 interface,let,yield,package,private等

七、嚴格模式下是禁止使用with的

Event Loop 是什麼?

宏任務:包括總體代碼script,setTimeout,setInterval

微任務:Promise.then(非new Promise),process.nextTick(node中)

事件的執行順序,是先執行宏任務,而後執行微任務,這個是基礎,任務能夠有同步任務和異步任務,同步的進入主線程,異步的進入Event Table並註冊函數,異步事件完成後,會將回調函數放入Event Queue中(宏任務和微任務是不一樣的Event Queue),同步任務執行完成後,會從Event Queue中讀取事件放入主線程執行,回調函數中可能還會包含不一樣的任務,所以會循環執行上述操做。  

事件機制

1.捕獲階段 (即由根節點流向子節點,檢測每一個節點是否註冊了監聽器) 

2.目標階段 (激發在目標對象自己註冊的監聽程序)  

3.冒泡階段 (從目標節點到根節點,檢測每一個節點是否註冊了監聽器)。事件流在此階段觸發

阻止冒泡

w3c方法是e.stopPropation() 

IE中方法是window.event.cancelBubble = true

阻止事件默認行爲

w3c方法是e.preventDefault 

IE中方法是window.event.returnValue = false

Load 和 DOMContentLoaded 區別

Load 事件觸發表明頁面中的 DOM,CSS,JS,圖片已經所有加載完畢。

DOMContentLoaded 事件觸發表明初始的 HTML 被徹底加載和解析,不須要等待 CSS,JS,圖片加載。

先觸發DOMContentLoaded事件,後觸發load事件。

SetTimeOut與requestAnimationFrame

與setTimeout相比,requestAnimationFrame最大的優點是由系統來決定回調函數的執行時機。具體一點講,若是屏幕刷新率是60Hz,那麼回調函數就每16.7ms被執行一次,若是刷新率是75Hz,那麼這個時間間隔就變成了1000/75=13.3ms,換句話說就是,requestAnimationFrame的步伐跟着系統的刷新步伐走。它能保證回調函數在屏幕每一次的刷新間隔中只被執行一次,這樣就不會引發丟幀現象,也不會致使動畫出現卡頓的問題。

onload事件和ready事件的區別

//window.onload函數在頁面全部內容加載完成後觸發,只能執行最後一個window.onload函數
$(window).load(function(){         //等價於window.onload = function
    alert("加載完成");
});
 
//ready函數在DOM樹加載完成後觸發,能夠同時執行多個ready函數。
$(document).ready(function(){      
//$(document)能夠簡寫成$(),  $(document).ready函數能夠簡寫成$(function(){})
    alert("加載完成");
});複製代碼

Sort原理

sort() 方法用於對數組的元素進行排序,並返回數組。

默認排序順序是根據字符串UniCode碼。

由於排序是按照字符串UniCode碼的順序進行排序的,因此首先應該把數組元素都轉化成字符串(若有必要),以便進行比較。

JS中substr與substring的區別?

js中substr和substring都是截取字符串中子串,很是相近,能夠有一個或兩個參數。

語法:substr(start [,length]) 第一個字符的索引是0,start必選 length可選

   substring(start [, end]) 第一個字符的索引是0,start必選 end可選

javascript中childNodes與children的區別?

children是Element的屬性,childNodes是Node的屬性 

function func(){}和var b=function(){}的區別

1 var func =function(){} ,即和 var 變量的特性 同樣。 func 變量名提早,可是不會初始化,直到執行到初始化代碼。

2 function func(){} 變量名 和方法體 都會提早到 頂部執行。

webworker

(1)同源限制

分配給 Worker 線程運行的腳本文件,必須與主線程的腳本文件同源。

(2)DOM 限制

Worker 線程所在的全局對象,與主線程不同,沒法讀取主線程所在網頁的 DOM 對象,也沒法使用documentwindowparent這些對象。可是,Worker 線程能夠navigator對象和location對象。

(3)通訊聯繫

Worker 線程和主線程不在同一個上下文環境,它們不能直接通訊,必須經過消息完成。

(4)腳本限制

Worker 線程不能執行alert()方法和confirm()方法,但可使用 XMLHttpRequest 對象發出 AJAX 請求。

(5)文件限制

Worker 線程沒法讀取本地文件,即不能打開本機的文件系統(file://),它所加載的腳本,必須來自網絡。

建立對象create與{}區別

Object.create()方法接受兩個參數:Object.create(obj,propertiesObject) ;

{}繼承自Object;

模擬實現 new 操做符

function newOperator(ctor){
    if(typeof ctor !== 'function'){
      throw 'newOperator function the first param must be a function';
    }
    // ES6 new.target 是指向構造函數
    newOperator.target = ctor;
    // 1.建立一個全新的對象,
    // 2.而且執行[[Prototype]]連接
    // 4.經過`new`建立的每一個對象將最終被`[[Prototype]]`連接到這個函數的`prototype`對象上。
    var newObj = Object.create(ctor.prototype);
    // ES5 arguments轉成數組 固然也能夠用ES6 [...arguments], Aarry.from(arguments);
    // 除去ctor構造函數的其他參數
    var argsArr = [].slice.call(arguments, 1);
    // 3.生成的新對象會綁定到函數調用的`this`。
    // 獲取到ctor函數返回結果
    var ctorReturnResult = ctor.apply(newObj, argsArr);
    // 小結4 中這些類型中合併起來只有Object和Function兩種類型 typeof null 也是'object'因此要不等於null,排除null
    var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;
    var isFunction = typeof ctorReturnResult === 'function';
    if(isObject || isFunction){
        return ctorReturnResult;
    }
    // 5.若是函數沒有返回對象類型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`),那麼`new`表達式中的函數調用會自動返回這個新的對象。
    return newObj;
}複製代碼

map,foreach 不一樣點

var array = [10,34,57,43,76];  
var res = array.forEach(function (item,index,input) {  
       input[index] = item*10;  
})  
console.log(res);//--> undefined;  
console.log(array);//--> 經過數組索引改變了原數組;複製代碼

var array = [10,34,57,43,76];  
var res = array.map(function (item,index,input) {  
       return item*10;   
})  
console.log(res);
console.log(array);//不變複製代碼

fetch瞭解過什麼?

瀏覽器如今支持Fetch API,能夠無須其餘庫就能實現Ajax,Fetch你想獲取資源,Fetch會返回Promise,因此在獲取資源後,可使用.then方法作你想作的。

第一個參數是設置請求方法(如post、put或del),Fetch會自動設置方法爲get。

第二個參數是設置頭部。由於通常使用JSON數據格式,因此設置ContentType爲application/json。

第三個參數是設置包含JSON內容的主體。由於JSON內容是必須的,因此當設置主體時會調用JSON.stringify。

將url轉換爲對象

function query(string) {
        // console.log(string) //  http://www.baidu.com?a=1&b=2&c=3
        let arr=string.split("?")  //截取字符,變爲僞數組
        // console.log(arr) // ["http://www.baidu.com", "a=1&b=2&c=3"]
        let arr1=arr[1].split("&"); //截取字符,變爲僞數組
        // console.log(arr1) // ["a=1", "b=2", "c=3"]
        let obj={}
        for (let i=0;i<arr1.length;i++){ //便利數組
            // console.log(i) //0 1 2
            let arr2=arr1[i].split("=") //截取arr1下面的每一個=
            // console.log(arr2)  ["a", "1"] ["b", "2"] ["c", "3"]
            if(arr2[0].indexOf("[")!= -1){
            		let arr3 =arr2[0].split("[")
            		if(obj.hasOwnProperty(arr3[0])){
            				obj[arr3[0]].push(arr2[1]);
            		}else{
            				obj[arr3[0]] = [];
            				obj[arr3[0]].push(arr2[1]);
            		}
            }else{
                 obj[arr2[0]]=arr2[1] //讓obj[arr2數組下的每一個下標爲0的]=arr2的數組下的每一個下標爲1的
            }
    }
    console.log(obj) //{a: "1", b: "2", c: "3"}
    console.log( typeof obj) //object
    return obj
}複製代碼

用sort給json對象數組排序

var jsonStudents = [
    {name:"Dawson", totalScore:"197", Chinese:"100",math:"97"},
    {name:"HanMeiMei", totalScore:"196",Chinese:"99", math:"97"},
    {name:"LiLei", totalScore:"185", Chinese:"88", math:"97"},
    {name:"XiaoMing", totalScore:"196", Chinese:"96",math:"100"},
    {name:"Jim", totalScore:"196", Chinese:"98",math:"98"},
    {name:"Joy", totalScore:"198", Chinese:"99",math:"99"}];
jsonStudents.sort(function(a,b){
    var value1 = a.totalScore,
        value2 = b.totalScore;
    if(value1 === value2){
        return b.Chinese - a.Chinese;
    }
    return value2 - value1;
});複製代碼

要求設計 LazyMan 類,實現如下功能

LazyMan('Tony');
// Hi I am Tony

LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony
// 等待了10秒...
// I am eating lunch

LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

class LazyManClass {
    constructor(name) {
        this.taskList = [];
        this.name = name;
        console.log(`Hi I am ${this.name}`);
        setTimeout(() => {
            this.next();
        }, 0);
    }
    eat (name) {
        var that = this;
        var fn = (function (n) {
            return function () {
                console.log(`I am eating ${n}`)
                that.next();
            }
        })(name);
        this.taskList.push(fn);
        return this;
    }
    sleepFirst (time) {
        var that = this;
        var fn = (function (t) {
            return function () {
                setTimeout(() => {
                    console.log(`等待了${t}秒...`)
                    that.next();
                }, t * 1000);  
            }
        })(time);
        this.taskList.unshift(fn);
        return this;
    }
    sleep (time) {
        var that = this
        var fn = (function (t) {
            return function () {
                setTimeout(() => {
                    console.log(`等待了${t}秒...`)
                    that.next();
                }, t * 1000); 
            }
        })(time);
        this.taskList.push(fn);
        return this;
    }
    next () {
        var fn = this.taskList.shift();
        fn && fn();
    }
}
function LazyMan(name) {
    return new LazyManClass(name);
}
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(4).eat('junk food');複製代碼
相關文章
相關標籤/搜索