前端面试——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');复制代码
相关文章
相关标签/搜索