web前端面试题合辑(三)

JavaScript篇

Javascript很多考题都出自红宝书(JavaScript高级程序设计)

  • JS 有哪些数据类型?

    • 基本数据类型:String Boolean Number Undefined Null (本质上null就是一个占位用的对象)ES6新增了Symbol(创建后独一无二且不可变的数据类型)
    • 引用数据类型:Object狭义的对象Array,Function
  • 是否可以使用 typeof bar === 'object' 来检测bar是不是object类型,有何风险?

    1. 有风险。typeof 只能检测出String,Number,Boolean,Undefined,Object,Function这六种类型.
    2. typeof null返回的也是Object,因为null本质上就是一个占位的对象.另一方面,数组 Array也不能用typeof检测数据类型,因为同样会返回Object.
  • 认清Array的方法:

    1. console.log( bar instanceof Array) // 如果数组,返回true
    2. console.log( Array.isArray(bar)) //ES5方法
    3. console.log( Object.prototype.toString.call(bar) === '[object Array]')
    4. 终极判断数据类型的方法:
      //可以判断的类型有 Number, String, Boolean, Undefined, Null,Object,Function,Array
      console.log( Object.prototype.toString.call(arr).slice(8, -1));
      console.log( Object.prototype.toString.call(arr).slice(8, -1) === 'Array'])
      
  • 什么是window对象? 什么是document对象?

    • window对象是指浏览器打开的窗口。
    • document对象是Documentd对象(HTML 文档对象)的一个只读引用,window对象的一个属性。
  • undefinednull的区别

    • undefined代表定义未赋值
    • null表示定义并且赋值了,只是值为null
  • 介绍js有哪些内置对象?

    • 数据封装类对象:Object、Array、Boolean、NumberString
    • 其他对象:Function、Arguments、Math、Date、RegExp、Error
  • 内存溢出与内存泄露

    1. 内存溢出
      • 一种程序运行出现的错误
      • 当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误
    2. 内存泄露
      • 占用的内存没有及时释放
      • 内存泄露积累多了就容易导致内存溢出
      • 常见的内存泄露:
        • 意外的全局变量
        • 没有及时清理的计时器或回调函数
        • 闭包
  • new操作符具体干了什么呢?
    1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
    2. 属性和方法被加入到 this 引用的对象中。
    3. 新创建的对象由 this 所引用,并且最后隐式的返回 this 。

    var obj = {};
    obj.__proto__ = Base.prototype;
    Base.call(obj);
    
  • 对象的创建模式?

    • Object构造函数模式
      /*
      	一个人: name:"Tom", age: 12
      */
      var p = new Object()  //先创建空Object对象
      p = {} //此时内部数据是不确定的
      p.name = 'Tom'// 再动态添加属性/方法
      p.age = 12
      p.setName = function (name) {
                      this.name = name
                  }
      //测试
      console.log(p.name, p.age)//Tom 12
      p.setName('Bob')
      console.log(p.name, p.age)//Bob 12
      
      • 对象字面量模式
      var p = {
      	    name: 'Tom',
      	    age: 12,
      	    setName: function (name) {
      	        this.name = name
      	    }
      	}		
      	//测试
      	console.log(p.name, p.age)
      	p.setName('JACK')
      	console.log(p.name, p.age)
      
    • 工厂函数:通过工厂函数动态创建对象并返回(内置对象)
      function createPerson(name, age) { //返回一个对象的函数===>工厂函数
      var obj = {
          name: name,
          age: age,
          setName: function (name) {
                      this.name = name
                  }
      }
          return obj
      }
      
      // 创建2个人
      var p1 = createPerson('Tom', 12)
      var p2 = createPerson('Bob', 13)
      
    • 构造函数模式
      function Person(name, age) {
          this.name = name;
          this.age = age;
          this.setName = function(name){this.name=name;};
      }
      new Person('tom', 12);
      
    • 构造函数+原型的组合模式
      function Person(name, age) {
          this.name = name;
          this.age = age;
      }
      Person.prototype.setName = function(name){this.name=name;};
      new Person('tom', 12);
      
  • JavaScript原型,原型链 ? 有什么特点?

    • 函数的prototype属性(显示原型属性): 在定义函数时自动添加的, 默认值是一个空Object实例对象(独独Object不满足)
    • 所有函数都是Function的实例对象(包含Function)
      console.log(Function.__proto__===Function.prototype)//true
    • Object的原型对象是原型链尽头
      console.log(Object.prototype.__proto__) // null
    • 所有实例对象都有一个特别的属性:__proto__(隐式原型属性)在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
    • 原型链:
      • 访问一个对象的属性时,
      • 先在自身属性中查找,找到返回
      • 如果没有, 再沿着__proto__这条链向上查找, 找到返回
      • 如果最终没找到, 返回undefined
      • 别名: 隐式原型链
      • 作用: 查找对象的属性(方法)
      • 这样通过__proto__属性就形成了一个链的结构---->原型链
      • 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找
  • Javascript如何实现继承?

    • 1 构造继承 2 原型继承 3 实例继承 4 拷贝继承
    //原型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);//得到被继承的属性
    
  • 上下文作用域

全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节。
当需要从局部函数查找某一属性或方法时,如果当前作用域没有找到,就会上溯到上层作用域查找,
直至全局函数,这种组织形式就是作用域链

  • 谈谈This(上下文)对象的理解。

    • 我们每次调用函数时,解析器都会将一个上下文对象作为隐含的参数传递进函数。使用this来引用上下文对象,根据函数的调用形式不同,this的值也不同。
    • this的不同的情况:
      1. 以函数的形式调用时,thiswindow
      2. 以方法的形式调用时,this就是调用方法的对象
      3. 以构造函数的形式调用时,this就是新创建的对象
      4. 使用callapply调用时,this时指定的那个对象
      5. 在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window
  • callapply的区别?

    • 这两个方法都是函数对象的方法需要通过函数对象来调用
    • 通过两个方法可以直接调用函数,并且可以通过第一个实参来指定函数中this
    • 不同的是call是直接传递函数的实参而apply需要将实参封装到一个数组中传递
  • Javascript中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?

    • hasOwnProperty
  • 什么是立即执行函数?使用立即执行函数的目的是什么?

    • 概念:1. 声明一个匿名函数 2. 马上调用这个匿名函数
    • 为什么要用括号把函数包起来:为了兼容JS的语法
    • 作用: 隐藏内部实现 不污染外部命名空间(创建一个独立的作用域,这个作用域里面的变量,外面访问不到(即避免「变量污染」))
    • 以这个面试题为例,用立即执行函数给每个 li 创造一个独立作用域即可
    var liList = ul.getElementsByTagName('li')
        for(var i=0; i<6; i++){
            function(j){
                liList[j].onclick = function(){
                    alert(j) // 0、1、2、3、4、5
           		 }
       		 }(i)
    	 }
    //在立即执行函数执行的时候,i 的值被赋值给 j,此后 j 的值一直不变。
    //i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的j「分别」
    //是 0、1、2、3、4、5。以上,就是立即执行函数的基本概念。
    
  • JSON 的了解?

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小.如:{"age":"12", "name":"back"}

JSON字符串转换为JSON对象:
	 var obj =eval('('+ str +')');
	 var obj = str.parseJSON();
	 var obj = JSON.parse(str);
	
	 JSON对象转换为JSON字符串:
	 var last=obj.toJSONString();
	 var last=JSON.stringify(obj);
  • NaN是什么?它是什么类型?如何检测一个变量是不是NaN?
    • 答案:NaN(not a Number),但是实际上它是Number类型,typeof NaN 会返回 number.这个东西比较厉害,因为
    • NaN === NaN //false
    • 你会发现它自己都不等于它自己,因此判断变量是否是它,不能使用 === ,可以使用isNaN()方法
//检查变量是否是NaN
   isNaN(bar);
   Object.is(bar,NaN); //ES6方法,这个方法会修正JS中的一些小bug
   /* Object.is()方法,要求严格相等,且Object.is(NaN,NaN)会返回true */
这两个方法结合起来用的作用在于判断变量 bar 是一个 NaN非数值,但不是NaN本身这个数
/*补充一个*/
var a = b = 3;
//实际上等同于
var a=b; 
	b=3;
  • 数组的filter,以下输出结果是什么(2018拼多多前端原题)
var arr = [1,2,3];
arr[10] = 9;
arr.filter((item)=> {
    return item === undefined?
})

//答案
[]

解析: 是的,答案的确是[],不是[undefined x 7]。 首先,看下前两句执行后,arr是什么

console.log(arr)
//[1,2,3, emptyx7, 9]
console.log(arr[5])
//undefined

undefined和数组保留的empty插槽并不是等同的,即使我们打印出相应的数据会显示undefined,但是与js的undefined是不同的,除了arr.filter,包括arr.map()函数都是会保留empty插槽的。

  • JS小数计算不准确的bug
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 == 0.3);

//答案: 0.30000000000000004
        false

解析: 详细的解析见连接,这里说一下解决办法 0.1+0.2 != 0.3

//解决办法
parseFloat((0.1+0.2).toFixed(10));
  • 讨论实现判断变量是否是整数的函数isInter(x)的实现
    答案: 在ES6中,是有现成的方法Number.isInteger可以使用的。如果自己实现,思路是什么呢
//1 异或运算
function isInter(x) {
    return x ^ 0 === x
}
//2 取整
return Math.round(x) === x  //同样可以用floor ceil
//取余
return (typeof x === 'number')&&(x % 1 === 0)
  • 写一个sum方法,可以实现以下两种调用方式
console.log(sum(2,3)) //5
console.log(sum(2)(3)) //5

答案:

//方法1
var sum = function(x,y) {
    if(y === undefined) {
        return function(y) {
            return x + y;
        }
    }else {
        return x + y;
    }
}

//方法2
var sum = function(x){
    if( arguments.length === 1) {
        return function (y) {
            return x + y;
        }
    } else {
        console.log('here');
        return arguments[0] + arguments[1];
    }
}

  • 递归设计。 实现一个函数,给该函数一个DOM节点,函数访问其所有子元素(所有子元素,不仅仅是直接子元素),每次访问子元素的时候,并为其传一个callback。
    访问一个DOM tree,是一个经典的深度优先搜索的算法
function Traverse(DOM,callback) {
    callback(DOM);
    var list = DOM.children;
    Array.prototype.forEach.apply(list,(item)=>{
        Traverse(item,callback); //递归
    })
}
  • Array数组的flat方法实现(2018网易雷火&伏羲前端秋招笔试)
    Array的方法flat很多浏览器还未能实现,请写一个flat方法,实现扁平化嵌套数组,如:
Array
var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

这个问题的实现思路和Deep Clone非常相似,这里实现如下:

Array.prototype.flat = function() {
    var arr = [];
    this.forEach((item,idx) => {
        if(Array.isArray(item)) {
            arr = arr.concat(item.flat()); //递归去处理数组元素
        } else {
            arr.push(item)   //非数组直接push进去
        }
    })
    return arr;   //递归出口
}

神秘力量的新解法

arr.prototype.flat = function() {
    this.toString().split(',').map(item=> +item )
}
1. toString方法,连接数组并返回一个字符串 '2,2,3,2,3,4'
2. split方法分割字符串,变成数组['2','2','3','2','3','4']
3. map方法,将string映射成为number类型2,2,3,2,3,4
  • 乱序的新定义
    以前面试的时候遇到要写打乱数组的题目,直接就是一句代码搞定
arr.sort(() => Math.random() - 0.5);

后面意识到这种方法并不能真正意义上去实现打乱
这是参考别人实现的方法:sort()方法无用?

function shuffle(arr) {
    /*
       即将它改造为一个对象,
      原来的值存储在键v中,同时给它增加一个键r,
      值为一个随机数,然后排序时比较这个随机数:
    */
    let new_arr = arr.map(i => ({v: i, r: Math.random()}));
    new_arr.sort((a, b) => a.r - b.r);
    arr.splice(0, arr.length, ...new_arr.map(i => i.v));

    return arr
  }
  /*这是测试代码*/
  let a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
  console.log(shuffle(a));
  • js延迟加载的方式有哪些?

    • defer(只支持IE)和async、创建script,插入到DOM中,加载完毕后callBack(用得最多)、按需异步载入js
  • 如何将浮点数点左边的数每三位添加一个逗号,如12000000.11转化为『12,000,000.11』?

    • (386485473.88).toLocaleString('en-US') // "386,485,473.88"
    //方法2:
    var separator=(num)=>{
        if(!num){
            return '0.00';
        };
        let str = parseFloat(num).toFixed(2);
        return str && str
            .toString()
            .replace(/(\d)(?=(\d{3})+\.)/g, function($0, $1) {
                return $1 + ",";
            });
    }
    
    separator(386485473.88) //"386,485,473.88"
    
  • 列举ES6新特性

    • 函数默认值、解构赋值 、模板字符串、let/const、新增函数库(Number String Array Obiect Math)、箭头函数
  • letconstvar的区别?

    • let
      • 无变量提升、有块级作用域、不能重复声明
    • const:
      • 无变量提升、有块级作用域、禁止重复声明
      • 禁止重复赋值、必须赋初始值
    • var 存在变量提升,可以重复声明,赋值
  • 箭头函数this的指向

    • 在箭头函数中,函数体内部没有自己的 this,默认在其内部调用 this 的时候,会自动查找其父级上下文的 this 对象(如果父级同样是箭头函数,则会按照作用域链继续向上查找)
  • 手写es6继承

  • promise的状态,链式调用,同步异步流程,唯一性

  • jsforfor in 循环它们之间的区别?

    • 遍历数组时的异同:
      • for循环 数组下标的typeof类型:number,
        -for in 循环数组下标的typeof类型:string
    • 遍历对象时的异同:
      • for循环 无法用于循环对象,获取不到obj.length;
      • for in循环遍历对象的属性时,原型链上的所有属性都将被访问,
      • 解决方案:使用hasOwnProperty方法过滤或Object.keys会返回自身可枚举属性组成的数组
  • js对数组去重,列出你的思路(两种以上)

    //第一种:原生方法去重,借助一个空数组实现去重,便于理解底层原理(unique函数带有参数)
    function unique(arr) {
    	let a = [];
    	//Array.prototype.forEach(function(item, index){}) : 遍历数组
    	arr.forEach((item,index)=>{
    		a.indexOf(item)===-1?a.push(item):'';
    	})
    	return a;
    };
    console.log(unique([1,3,3,5,7,1,1,3])); //[1,3,5,7];
    		 
    		 
     //第二种:
     /*第二种同上(unique函数,但是不带参数)
     	拓展:需要注意的一点就是此函数可以传多个数组,但是要看arguments[index]
     	决定执行哪个数组,默认是执行第一个*/
     function unique2() {
     	let a = [];
     	//Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
     	Array.from(arguments[1]).forEach((item,index)=>{
     		a.indexOf(item)===-1?a.push(item):'';
     	})
     	return a;
     }
     console.log(unique2([1,2,2,5,1,3,4,3],[1,1,5,55,4]);//[ 1, 5, 55, 4 ]
    		 
     //原理
     function unique2() {
     	let a = [];
     	//Array.prototype 可以换成[]
     	Array.prototype.forEach.call(arguments[0],(item,index)=>{
     		a.indexOf(item)===-1?a.push(item):'';
     	})
     	return a;
     }
     console.log(unique2([1,2,2,5,1,3,4,3],[1,1,5,55,4]);//[[ 1, 2, 5, 3, 4 ]]
    		 	
    //第三种方法:  Array.from方法可以将Set结构转为数组  
    //new Set + Array.from
    function unique3(arr) {
    		return Array.from(new Set(arr));
    	}
    	console.log(unique3([1,2,2,5,1,3,4,3]));
    		
    		
    //第四种方法:最简单
    let arr = [1,2,5,5,1,3,4,3,7,9,7];
    let uniq = [...new Set(arr)];
    console.log(uniq);
    		
    //第五种方法: 基于数组原型链的去重方法
    Array.prototype.uniq = function () {
    	let a = [];
    	this.forEach((item,index)=>{
    		a.indexOf(item)===-1?a.push(item):'';
    	})
    	return a;
    }
    console.log([3,1,4,1,1,3].uniq());
    
  • 什么是闭包?手写一个闭包

    • 包含被引用变量(函数)的对象(通过Chrome工具查看)
  • 请将下列b函数进行修改,保证每次调用a都能+1(考闭包):

    function b(){
        var a=1;
    };
    function b(){
        var a=1;
        return ()=>{
            a++;
            return a;
        }
    };
    let c = b();
    c(); //2
    c(); //3
    c(); //4
    
  • 删除数组最后一个元素,改变原数组的方法?

    • myArr.pop()
    • myArr.slice(myArr.length-1)
    • myArr.splice(myArr.length,1)
  • 列举常用的5个字符串操作方法

    • string.slice()
      • 可以从一个字符串中截取指定的内容,并将截取到内容返回,不会影响原变量
    • string.substring()
      • 和slice()基本一致,不同的是它不能接受负值作为参数,如果设置一个负值,则会自动修正为0,
        substring()中如果第二个参数小于第一个,自动调整位置
    • string.substr()
      • 和slice()基本一致,不同的是它第二个参数不是索引,而是截取的数量
    • string.trim() 去除前后空格
    • string.toUpperCase() 将字符串转换为大写并返回
    • string.toLowerCase() 将字符串转换为小写并返回
  • 列举常用的5个数组操作方法

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
  • 事件冒泡以及事件捕获。

  • 如何实现数组的随机排序?

/*第一种方法*/
var arr = [1,2,3,4,5,6,7,8,9,10];
  	function randSort1(arr){
  		for(var i = 0,len = arr.length;i < len; i++ ){
  			var rand = parseInt(Math.random()*len);
  			var temp = arr[rand];
  			arr[rand] = arr[i];
  			arr[i] = temp;
  		}
  		return arr;
  	}
  	console.log(randSort1(arr));
  	
/*第二种方法*/  	
var arr = [1,2,3,4,5,6,7,8,9,10];
  	arr.sort(function(){
  		return Math.random() - 0.5;
  	})
  	console.log(arr);
  	
/*第三种方法*/
var arr = [1,2,3,4,5,6,7,8,9,10];
  	function randSort2(arr){
  		var mixedArray = [];
  		while(arr.length > 0){
  			var randomIndex = parseInt(Math.random()*arr.length);
  			mixedArray.push(arr[randomIndex]);
  			arr.splice(randomIndex, 1);
  		}
  		return mixedArray;
  	}
  	console.log(randSort2(arr));
  • 排序方法
    /*冒泡排序*/		
    function bubbleSort (myArr) {
    	var len = myArr.length;
    	var i,j,stop;
    	for (i = 0; i < len; i++) {				
    		for (j = 0; stop = len-i, j < stop; j++) {
    			if (myArr[j] > myArr[j+1]) {										
    				[myArr[j],myArr[j+1]] = [myArr[j+1],myArr[j]];//交换元素
    			}
    		}
    		
    	}
    	return myArr;				
    }
    var res = bubbleSort([3,2,4,5,1]);
    console.log(res);//[1,2,3,4,5]
    	
    			
    /*选择排序*/
    function selectSort (myArr) {
    	var len = myArr.length,
    			min;
    	var i,j;
    	for (i = 0; i < len; i++) {
    		min = i;
    		for (j=i+1; j < len;j++) {
    			if (myArr[j]<myArr[min]) {
    				min = j;
    			}
    		}	
    		//如果i不是最小的,则互换
    		if (i!= min) {
    			[myArr[i],myArr[min]] = [myArr[min],myArr[i]];
    		}					
    	}		
    	return myArr;
    }
    var res = selectSort([3,2,4,5,1]);
    console.log(res);//[1,2,3,4,5]
    			
    			
    /*插入排序*/
    function insertionSort(arr) {
      for(let i = 1; i < arr.length; i++) {
        for(let j = 0; j < i; j++) {
          if(arr[i] < arr[j]) {
    				//在j位置新增一个元素arr[i]      	
            arr.splice(j, 0, arr[i]);
            //再把原来arr[i]所在的删除
            arr.splice(i+1, 1);
            break
          }
          console.log(arr); // 至今不明白为什么控制台输出是[2,3,4,5,1]
    
        }
      }
    }
    	var arr = [3,2,4,5,1];
    	insertionSort(arr);
    	console.log(arr); //[1,2,3,4,5]
    				
    /*归并排序*/
    function mergeSort(arr) {
      var merge = function(leftArr, rightArr) {
        var resultArr = []
        while(leftArr.length && rightArr.length) {
          resultArr.push(leftArr[0] <= rightArr[0] ? leftArr.shift() : rightArr.shift());
        }
        return resultArr.concat(leftArr).concat(rightArr)
      }
      if(arr.length < 2) return arr;
      let mid = parseInt(arr.length/2)  //取数组的中位下标,也可以用 arr.length >> 1
      return merge(mergeSort(arr.slice(0, mid)), mergeSort(arr.slice(mid)));
    }
    var res = mergeSort([3,2,4,5,1]);
    console.log(res);//[1,2,3,4,5]
    
    /*快速排序*/
    function quickSort(arr) {
    	  if (arr.length <= 1) {
        		return arr //递归的出口
      		}
    
    	  var left = [],
    	      right = [],
    	    current = arr.splice(0,1); //此时数组少了一个数(第一个)
    	  for (let i =0; i<arr.length; i++) {
    	    if (arr[i] < current) {
    	      left.push(arr[i]); // 放左边
    	    }else {
    	      right.push(arr[i]); //放右边
    	    }
    	  }
      return quickSort(left).concat(current,quickSort(right));
    }
    /*测试*/
    let res = quickSort([4,7,21,6,9,3,1]);
    console.log(res);//[1,3,4,6,7,9,21]
    
  • DOM操作——怎样添加、移除、移动、复制、创建和查找节点?
    这个面试的时候问了怎么用js动态控制插入节点
    (1)创建新节点
    createDocumentFragment() //创建一个DOM片段
    createElement() //创建一个具体的元素
    createTextNode() //创建一个文本节点
    (2)添加、移除、替换、插入
    appendChild()
    removeChild()
    replaceChild()
    insertBefore() //在已有的子节点前插入一个新的子节点
    (3)查找
    getElementsByTagName() //通过标签名称
    getElementsByName() //通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
    getElementById() //通过元素Id,唯一性
  • Promise 怎么使用?
    -ES6Promise是一个构造函数,用来生成promise实例。
    • 作用:
      • 解决回调地狱(回调函数的层层嵌套, 编码是不断向右扩展, 阅读性很差)
      • 能以同步编码的方式实现异步调用
    • 使用Promise:
      1. 创建promise对象
        let promise = new Promise((resolve, reject) => {
             //初始化promise状态为 pending
             //执行异步操作
             if(异步操作成功) {
                 resolve(value);//修改promise的状态为fullfilled
             } else {
                 reject(errMsg);//修改promise的状态为rejected
             }
         }) 
        
      2. 调用promisethen()
         promise.then(function(
                 result => console.log(result),
                errorMsg => alert(errorMsg)
                ));
         ---- --- --- ----- -------------------------
        promise.then(()=>{},()=>{}).then()...
        //使用promise封装处理ajax请求
        let request = new XMLHttpRequest();
        request.onreadystatechange = function () {
        
                                   }
        request.responseType = 'json';
        request.open("GET", url);
        request.send();
        
  • async/await语法了解吗?目的是什么

概念:真正意义上去解决异步回调的问题,同步流程表达异步操作(本质是Generator的语法糖)

	* 不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
	* 返回的总是Promise对象,可以用then方法进行下一步操作
	* async取代Generator函数的星号*,await取代Generator的yield
	* 语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
  • 如何用正则实现 string.trim() //去除字符串前后的空格
    • function trim(string){ return string.replace(/^\s+|\s+$/g, '')}
  • 写一个通用的事件侦听器函数。
// 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;
 		}
 	};
  • 刁钻代码题
  • a.x = a = {} 问 a.x 是多少? undefined
 var a = {n:1};
 var b = a;
 a.x = a = {n:2};
  • (a ==1 && a== 2 && a==3) 可能为true 吗?

    a = {
      value: 0,
      toString(){
        a.value += 1
        return a.value 
      }
    }
    
  • ["1", "2", "3"].map(parseInt) 答案是多少?详细解析

    • parseInt() 函数能解析一个字符串,并返回一个整数,需要两个参数 (val, radix),其中 radix 表示要解析的数字的基数。【该值介于2 ~ 36 之间,并且字符串中的数字不能大于radix才能正确返回数字结果值】;
    • 但此处 map传了 3 个 (element,index, array),我们重写parseInt函数测试一下是否符合上面的规则。
      function parseInt(str, radix) {
           return str+'-'+radix;
       };
       var a=["1", "2", "3"];
       a.map(parseInt);  // ["1-0", "2-1", "3-2"] 不能大于radix
       /* 因为二进制里面,没有数字3,导致出现超范围的radix赋值和不合法的进制解析,
       才会返回NaN所以["1", "2", "3"].map(parseInt) 答案也就是:[1, NaN, NaN]*/
      
  • 以下两个函数是否等价

function foo1()
{
  return {
      bar: "hello"
  };
}

function foo2()
{
  return 
  {
      bar: "hello"
  };
}
console.log(foo1()); // {bar : "hellp"}
console.log(foo2()); // undefined
答案:不等价!!
注意return 后面大括号的位置,第二个函数js会默认return 后面返回的东西(是空),等价于
return undefined
    {xxx}   //后面当然,当然是写了也白写	
  • 事件是?IE与火狐的事件机制有什么区别? 如何阻止冒泡?
    1. 我们在网页中的某个操作(有的操作对应多个事件)。例如:当我们点击一个按钮就会产生一个事件。是可以被 JavaScript侦测到的行为。
    2. 事件处理机制:IE是事件冒泡、Firefox同时支持两种事件模型,也就是:捕获型事件和冒泡型事件;
    3. ev.stopPropagation();(旧ie的方法 ev.cancelBubble = true;

  • 如何实现深拷贝?

    • 理想方案:(但不能处理函数数据)
      • function clone(object){ let _obj=JSON.parse(JSON.stringify(obj))}
    • 保险做法
      function clone(obj){    
        if(!obj&& typeof obj!== 'object'){      
          return;    
        }
        var newObj=obj.constructor===Object?{}:[];    
        for(var key in obj){              
          newObj[key] =(obj[key]&&typeof obj[key]==='object')?clone(obj[key]):obj[key];       
        }    
        return newObj;
      }
      
  • 你对重绘、重排的理解?

    • 首先网页数次渲染生成时,这个可称为重排;
    • 修改DOM、样式表、用户事件或行为(鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等等)这些都会导致页面重新渲染,那么重新渲染,就需要重新生成布局和重新绘制节点,前者叫做"重排",后者"重绘";
    • 减少或集中对页面的操作,即多次操作集中在一起执行;
    • 总之可以简单总结为:重绘不一定会重排,但重排必然为会重绘。
    • 更详细的可以看阮老师分析
  • 项目上线前,能做哪些优化?

    • 图片预加载,css样式表放在顶部且link链式引入,javascript放在底部body结束标签前;
    • 使用dns-prefetch对项目中用到的域名进行 DNS预解析,减少 DNS查询,如: ;
    • 减少http请求次数:图片静态资源使用CDN托管;API接口数据设置缓存,CSS Sprites/SVG Sprites, JSCSS源码压缩、图片大小控制合适,使用iconfont(字体图标)或SVG,它们比图片更小更清晰,网页Gzip压缩;
    • 减少DOM操作次数,优化javascript性能;
    • 减少 DOM元素数量,合理利用:after、:before等伪类;
    • 避免重定向、图片懒加载;
    • 前后端分离开发,资源按需加载,最好能做到首屏直出(即服务端渲染);
    • 避免使用CSS Expressioncss表达式)又称Dynamic properties(动态属性)
    • 多域名分发划分内容到不同域名,解决浏览器域名请求并发数问题,同时也解决了请求默认携带的cookie问题;
    • 尽量减少iframe 使用,它会阻塞主页面的渲染;
    • 对所有资源压缩 JavaScriptCSS字体图片等,甚至html;
  • DOM 清空子元素的方法


<input type="button" onclick="deleteChildren('#bbb');" value="删除所有子元素" />
function deleteChildren (a) {
	//获取父元素
	var parentNode = document.querySelector(a);
	//判断是否包含子元素
	if (parentNode.hasChildNodes()) {
		var len = parentNode.childNodes.length;//子元素的个数
		for (var i = 0; i < len; i++) {
			parentNode.removeChild(parentNode.childNodes[0]);//从第一个元素开始删除
		}
	}
}

参考文章在这

你可能感兴趣的:(学习随笔,基础干货)