web面试集合

在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的总体运行性能。致使这一问题的缘由是多方面的。首先,每一个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定全部事件处理程序而致使的DOM访问次数,会延迟整个页面的交互就绪时间。javascript

对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就能够管理某一类型的全部事件。例如,click事件会一直冒泡到document层次。也就是说,咱们能够为整个页面指定一个onclick事件处理程序,而没必要给每一个可单击的元素分别添加事件处理程序。html

 

事件委托(事件代理)是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,而后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;好比给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么咱们给最外面的div加点击事件,那么里面的ul,li,a作点击事件的时候,都会冒泡到最外层的div上,因此都会触发,这就是事件委托委托它们父级代为执行事件前端

当用事件委托的时候,根本就不须要去遍历元素的子节点,只须要给父级元素添加事件就行了,其余的都是在js里面的执行,这样能够大大的减小dom操做,这才是事件委托的精髓所在。vue

 

递归调用

本身调用本身,称为递归调用java

注意:下面这段代码运行会报错:Maximum call stack size exceedednode

错误直译过来就是“栈溢出”,出现这个错误的缘由是由于我进行了递归运算,可是忘记添加判断条件,致使递归无限循环下去jquery

 
function fun()
{
    // 本身调用本身,称为递归调用
    fun();
    console.log("m2");
}
fun();
 

解决办法以下:es6

 
(function a(x) {
    // The following condition 
    // is the base case.
    if ( ! x) {
        return;
    }
    a(--x);
})(10);
 

 

事件委托

http://www.javashuo.com/article/p-tsnncwfy-ec.htmlweb

每一个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,天然性能就越差了(内存不够用,是硬伤,哈哈),好比上面的100个li,就要占用100个内存空间,若是是1000个,10000个呢,那只能说呵呵了,若是用事件委托,那么咱们就能够只对它的父级(若是只有一个父级)这一个对象进行操做,这样咱们就须要一个内存空间就够了,是否是省了不少,天然性能就会更好。面试

Event对象提供了一个属性叫target,能够返回事件的目标节点,咱们成为事件源,也就是说,target就能够表示为当前的事件操做的dom,可是不是真正操做dom,固然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里咱们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,咱们须要转成小写再作比较(习惯问题):

 
     window.onload = function(){
              var oUl = document.getElementById("ul1");
              oUl.onclick = function(ev){
                    var ev = ev || window.event;
                      var target = ev.target || ev.srcElement;
                    if(target.nodeName.toLowerCase() == 'li'){
                            alert(123);
                 alert(target.innerHTML);
                    }
              }
        }
 

http://www.javashuo.com/article/p-tsnncwfy-ec.html

 

IE使用的是事件冒泡,其余浏览器是事件捕获 

IE提出的是冒泡流,而网景提出的是捕获流,后来在W3C组织的统一之下,JS支持了冒泡流和捕获流,可是目前低版本的IE浏览器仍是只能支持冒泡流(IE6,IE7,IE8均只支持冒泡流),因此为了可以兼容更多的浏览器,建议你们使用冒泡流。

 

由此能够知道
  一、一个完整的JS事件流是从window开始,最后回到window的一个过程
  二、事件流被分为三个阶段(1~5)捕获过程、(5~6)目标过程、(6~10)冒泡过程

 

闭包就是可以读取其余函数内部变量的函数。

闭包就是一个函数引用另一个函数的变量,由于变量被引用着因此不会被回收,所以能够用来封装一个私有变量。这是优势也是缺点,没必要要的闭包只会徒增内存消耗!

因为在javascript中,只有函数内部的子函数才能读取局部变量,因此说,闭包能够简单理解成“定义在一个函数内部的函数“。

因此,在本质上,闭包是将函数内部和函数外部链接起来的桥梁

闭包的使用:函数做为返回值,函数做为参数传递

 
// 函数做为返回值
function fn() { var max = 10; return function bar(x) {     if (x > max) {       console.log(x);     };   }; }; var abc = fn(); abc(12);
 

 

原型也是对象叫原型对象。

每一个对象都有一个__proto__属性,指向建立该对象的函数的prototype

每一个函数function都有一个prototype,即原型。每一个对象都有一个__proto__,可成为隐式原型。

做用域最大的用处就是隔离变量,不一样做用域下同名变量不会有冲突

http://www.javashuo.com/article/p-xddmkdtm-n.html

http://www.cnblogs.com/wangfupeng1988/p/3977924.html    最全的理解JavaScript原型闭包等概念,一共16篇文章

 

一切(引用类型)都是对象,对象是属性的集合

 

对象都是经过函数来建立的

 

每一个函数都有一个属性叫作prototype,这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫作constructor的属性,指向这个函数自己。每一个对象都有一个__proto__,可成为隐式原型,指向建立该对象的函数的prototype

 

对象的__proto__指向的是建立它的函数的prototype,就会出现:Object.__proto__ === Function.prototype

 

instanceof表示的就是一种继承关系,或者原型链的结构

 

访问一个对象的属性时,先在基本属性中查找,若是没有,再沿着__proto__这条链向上找,这就是原型链

 

那么咱们在实际应用中如何区分一个属性究竟是基本的仍是从原型中找到的呢?你们可能都知道答案了——hasOwnProperty

 

全部的对象的原型链都会找到Object.prototype,所以全部的对象都会有Object.prototype的方法。这就是所谓的“继承”

 

在一段js代码拿过来真正一句一句运行以前,浏览器已经作了一些“准备工做”,其中就包括对变量的声明,而不是赋值。变量赋值是在赋值语句执行的时候进行的。

 

在“准备工做”中完成了哪些工做:

  • 变量、函数表达式——变量声明,默认赋值为undefined;
  • this——赋值;
  • 函数声明——赋值;

这三种数据的准备状况咱们称之为“执行上下文”或者“执行上下文环境”。

 

函数每被调用一次,都会产生一个新的执行上下文环境。由于不一样的调用可能就会有不一样的参数。

 

函数在定义的时候(不是调用的时候),就已经肯定了函数体内部自由变量的做用域

 

在执行代码以前,把将要用到的全部的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。

 

在函数中this到底取何值,是在函数真正被调用执行的时候肯定的,函数定义的时候肯定不了。由于this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。

其实,不只仅是构造函数的prototype,即使是在整个原型链中,this表明的也都是当前对象的值。

this指向的是函数在运行时的上下文,既不是函数对象自己,也不是函数声明时所在做用域

 

执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再从新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。

其实这是一个压栈出栈的过程——执行上下文栈

 

javascript除了全局做用域以外,只有函数能够建立的做用域。

 

因此,咱们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其余地方都不要出现变量声明。并且建议用“单var”形式。

 

做用域最大的用处就是隔离变量,不一样做用域下同名变量不会有冲突

 

做用域在函数定义时就已经肯定了。而不是在函数调用时肯定。

 

做用域只是一个“地盘”,一个抽象的概念,其中没有变量。要经过做用域对应的执行上下文环境来获取变量的值。

 

同一个做用域下,不一样的调用会产生不一样的执行上下文环境,继而产生不一样的变量的值。因此,做用域中变量的值是在执行过程当中产生的肯定的,而做用域倒是在函数建立时就肯定了。

 

若是要查找一个做用域下某个变量的值,就须要找到这个做用域对应的执行上下文环境,再在其中寻找变量的值

 

在A做用域中使用的变量x,却没有在A做用域中声明(即在其余做用域中声明的),对于A做用域来讲,x就是一个自由变量。

 

自由变量的取值:要到建立这个函数的那个做用域中取值——是“建立”,而不是“调用”,切记切记——其实这就是所谓的“静态做用域”。

 

闭包应用的两种状况便可——函数做为返回值,函数做为参数传递。

 

使用闭包会增长内容开销

 

多态意味着同名方法的实现依据类型有所改变,在JS中只须要在“子类”Student的prototype定义同名方法便可,由于原型链是单向的,不会影响上层的原型。

 

instanceof从字面意上来讲就是判断当前对象是不是后面的实例, 实际上其做用是判断一个函数的原型是否在对象的原型链上

 

构造函数自己其实就是普通的函数,只是咱们专门用它来实现了构造的功能,因此专门起了一个名字叫构造函数,任何函数均可以成为构造函数,这取决于你调用函数的方式,当使用了New的方式调用就成了构造函数。

 

原型:每一个函数都有一个 prototype属性,它是一个 对象,也称做原型对象,咱们能够把方法和属性写在它上面(不过原型对象不只仅有咱们写的属性和方法,还有别的,下面会介绍),而经过这个函数建立出来的实例对象,都能 共享这个原型对象下的方法和属性。因此咱们只须要把想要共享的东西放在函数的prototype下,不想共享的东西经过构造函数来建立就能够了。
 
每一个实例化对象都有_proto_属性,它是一个指针,指向函数的prototype,也就是保存了它的地址。( JS中任何对象的值都是保存在堆内存中,咱们声明的变量只是一个指针,保存了这个对象的实际地址,因此有了地址就能找到对象),
因此总得来讲,每一个实例化对象都有_proto_属性,保存了构造函数的原型对象的地址,经过这个属性就能够拥有原型对象下的全部属性和方法, _proto_属性实际就是实例化对象和原型对象之间的链接
 
继承:在不改变源程序的基础上进行扩充,原功能得以保存,而且对子程序进行扩展,避免重复代码编写
 
原生JS是弱类型语言,没有多态概念
 
构造函数的方法有一些规范:
1)函数名和实例化构造名相同且大写,(PS:非强制,但这么写有助于区分构造函数和
普通函数);
2)经过构造函数建立对象,必须使用new 运算符。
 
构造函数和普通函数的惟一区别,就是他们调用的方式不一样。只不过,构造函数也是函数,必须用new 运算符来调用,不然就是普通函数。
 

原型prototype解决了消耗内存问题。固然它也能够解决this做用域等问题。

咱们常常把属性(一些在实例化对象时属性值改变的),定义在构造函数内;把公用的方法添加在原型上面,也就是混合方式构造对象(构造方法+原型方式)

 

咱们把这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫作原型链。以下图:

2dbd5870d84a471896d69f7d1980ae63

 

继承是面向对象中一个比较核心的概念。其余正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而ECMAScript 只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。

在JavaScript 里,被继承的函数称为超类型(父类,基类也行,其余语言叫法),继承的函数称为子类型(子类,派生类)

 

 

递归调用

函数的递归就是在函数中调用自身。使用递归函数必定要注意,处理不当就会进入死循环。递归函数只有在特定的状况下使用 ,好比阶乘问题

 

各类循环写法

 
       var arr = [2,4,6,8,10,12,14,16,18,20];
for (var i = 0; i < arr.length; i++) { console.log(arr[i]); } for(var i in arr){ console.log(arr[i]); } arr.forEach(function (val,index) { console.log(val); });
       // jquery写法 $.each(arr, function(index,val) { console.log(val); });
 

 

数组去重

 
       Array.prototype.unique = function() {
                var result = [];
                this.forEach(function(v) {
                    if(result.indexOf(v) < 0) {
                        result.push(v);
                    }
                });
                return result;
            }
 

 

清除浮动

 
/* 1.添加新元素 */
<div class="outer">
  <div class="div1"></div>
  <div class="div2"></div>
  <div class="div3"></div>
  <div class="clearfix"></div>
</div>
.clearfix {
  clear: both;
}
/* 2.为父元素增长样式 */
.clearfix {
  overflow: auto;
  zoom: 1; // 处理兼容性
}
/* 3.:after 伪元素方法 (做用于父元素) */
.outer {
  zoom: 1;
  &:after {
    display: block;
    height: 0;
    clear: both;
    content: '.';
    visibillity: hidden;
  }
}
 

 

面试题

http://www.javashuo.com/article/p-tqzaceew-bx.html

 

渐进加强(Progressive Enhancement):一开始就针对低版本浏览器进行构建页面,完成基本的功能,而后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。

优雅降级(Graceful Degradation):一开始就构建站点的完整功能,而后针对浏览器测试和修复。好比一开始使用 CSS3 的特性构建了一个应用,而后逐步针对各大浏览器进行 hack 使其能够在低版本浏览器上正常浏览。

 

MVC

http://www.javashuo.com/article/p-tbszcdib-a.html

咱们让每个层次去关注并作好一件事情,层与层之间保持松耦合,咱们能够对每个层次单独作好测试工做。如此,咱们可让代码更具可维护性。

使用MVC的设计思想,编写出高维护性的前端程序,主要目的是分离视图和模型

MVC是一种设计模式,它将应用划分为3个部分:数据(模型)、展示层(视图)和用户交互(控制器)。换句话说,一个事件的发生是这样的过程:
  1. 用户和应用产生交互。
  2. 控制器的事件处理器被触发。
  3. 控制器从模型中请求数据,并将其交给视图。
  4. 视图将数据呈现给用户。
咱们不用类库或框架就能够实现这种MVC架构模式。关键是要将MVC的每部分按照职责进行划分,将代码清晰地分割为若干部分,并保持良好的解耦。这样能够对每一个部分进行独立开发、测试和维护。

 

Vue

http://www.javashuo.com/article/p-brudryzf-hq.html   剖析vue原理

http://www.javashuo.com/article/p-ttwlcyrf-be.html      vue双向绑定原理

实现数据绑定的作法有大体以下几种:

发布者-订阅者模式(backbone.js)

脏值检查(angular.js) 

数据劫持(vue.js)

数据劫持: vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,经过Object.defineProperty()来劫持各个属性的settergetter,在数据变更时发布消息给订阅者,触发相应的监听回调。

 

Object.defineProperty

http://www.javashuo.com/article/p-wkgxxnxj-es.html

 

ES6

https://blog.csdn.net/qq_35480270/article/details/53978449

Babel是一个普遍使用的ES6转码器,能够将ES6代码转为ES5代码,从而在现有环境执行。

ES5只有全局做用域和函数做用域,没有块级做用域,这带来不少不合理的场景。第一种场景就是你如今看到的内层变量覆盖外层变量。而let则实际上为JavaScript新增了块级做用域。用它所声明的变量,只在let命令所在的代码块内有效。

另一个var带来的不合理场景就是用来计数的循环变量泄露为全局变量

const有一个很好的应用场景,就是当咱们引用第三方库的时声明的变量,用const来声明能够避免将来不当心重命名而致使出现bug

 

 
class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        console.log(this.type + ' says ' + say)
    }
}
 
let animal = new Animal()
animal.says('hello') //animal says hello
 
class Cat extends Animal {
    constructor(){
        super()
        this.type = 'cat'
    }
}
 
let cat = new Cat()
cat.says('hello') //cat says hello
 

上面代码首先用class定义了一个“类”,能够看到里面有一个constructor方法,这就是构造方法,而this关键字则表明实例对象。简单地说,constructor内定义的方法和属性是实例对象本身的,而constructor外定义的方法和属性则是全部实力对象能够共享的。

Class之间能够经过extends关键字实现继承,这比ES5的经过修改原型链实现继承,要清晰和方便不少。上面定义了一个Cat类,该类经过extends关键字,继承了Animal类的全部属性和方法。

super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,不然新建实例时会报错。这是由于子类没有本身的this对象,而是继承父类的this对象,而后对其进行加工。若是不调用super方法,子类就得不到this对象。

ES6的继承机制,实质是先创造父类的实例对象this(因此必须先调用super方法),而后再用子类的构造函数修改this。

 

箭头函数:

它简化了函数的书写。操做符左边为输入的参数,而右边则是进行的操做以及返回的值Inputs=>outputs。

  • 不须要 function 关键字来建立函数
  • 省略 return 关键字
  • 继承当前上下文的 this 关键字
 
class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout( () => {
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}
 var animal = new Animal()
 animal.says('hi')  //animal says hi
 

当咱们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
并非由于箭头函数内部有绑定this的机制,实际缘由是箭头函数根本没有本身的this,它的this是继承外面的,所以内部的this就是外层代码块的this。

 

解构赋值:

数组和对象是JS中最经常使用也是最重要表示形式。为了简化提取信息,ES6新增了解构,这是将一个数据结构分解为更小的部分的过程

 
// 数组的解构赋值
let [name,age,sex] = ['牛牛',24,'女'] console.log(name + '--' + age + '---' + sex)

// 多层数组解构赋值
let [arr1,arr2,[arr3,arr4,[arr5,arr6]]] = [1,2,[3,4,[5,6]]];
console.log(arr1)
// 对象的解构赋值,注意名称必须和对象key一致
let {name,sex,age} = {name : '东东', sex : '男', age : 23}
console.log(name + '--' + age + '---' + sex)

//基本类型的解构赋值
let [a,b,c,d,e] = '我是中国人'
console.log(a)
 

 

展开运算符

ES6中另一个好玩的特性就是Spread Operator 也是三个点儿...接下来就展现一下它的用途。

组装对象或者数组

 
   //数组
    const color = ['red', 'yellow']
    const colorful = [...color, 'green', 'pink']
    console.log(colorful) //[red, yellow, green, pink]
    
    //对象
    const alp = { fist: 'a', second: 'b'}
    const alphabets = { ...alp, third: 'c' }
    console.log(alphabets) //{ "fist": "a", "second": "b", "third": "c" }
 

 set、map数据集合

set集合和数组的区别就是数组能够有重复数据,可是set集合是没有重复数据的。

获取集合长度size属性

 
       let set = new Set(['张三','李四','王五']);
            
            // 集合长度
            console.log(set.size);
            
            // 添加
            set.add('牛雨晴');
            console.log(set);
            
            set.delete('张三');
            console.log(set);
            
            // has
            console.log(set.has('王五'))
            
            // 清空集合
            set.clear();
            console.log(set);
 

 

map也是没有重复数据的

map的话存放的是对象

 
       let obj1 = {'a':'1','b':'2'}
            let map = new Map([
                ['name','张三'],
                ['age',20],
                ['sex','男'],
                [obj1,'今每天气很好'],
                [[1,2,3],'适合敲代码']
            ]);
            console.log(map);
            console.log(map.size);
            
            // set方法
            map.set('friends','小花花')
            console.log(map)
            
            // get方法
            console.log(map.get('name'));
            
            // delete方法
            map.delete('name');
            console.log(map);
            
            // keys方法
            console.log(map.keys());
            
            // values方法
            console.log(map.values());
            
            // entries方法,获得键值对
            console.log(map.entries());
            
            // 对map进行遍历
            map.forEach(function (value,index) {
                console.log(index + ':' + value)
            })
 

 

symbol解决命名冲突问题

 

基于原型,经过构造函数实现面向对象

es6经过class实现面向对象

es6的class其实就是一个语法糖,底层仍是基于原型和构造函数实现面向对象

 

模板字符串 ` ` 包裹

 
       // 模板字符串 ``包裹
            // 内容使用${}
            let str = 'hello world';
            let className = 'text';
            let html = `
                        <html>
                            <head></head>
                            <body>
                                <p class='${className}'>${str}</p>
                            </body>
                        </html>
                        
            `;
            console.log(html);
 

 

新增字符串方法

1.includes,是否包含某个字符串

2.startsWith,是不是以某个字母开头

console.log('hello'.includes('o'));

console.log('hello'.startsWith('o'));

 

异步执行能够用回调函数来实现。

 

Promise

https://www.jianshu.com/p/c98eb98bd00c

Promises是处理异步操做的一种模式,以前在不少三方库中有实现,好比jQuery的deferred 对象。当你发起一个异步请求,并绑定了.when(), .done()等事件处理程序时,其实就是在应用promise模式。

Promise是异步编程的一种解决方案,它有三种状态,分别是pending-进行中、resolved-已完成、rejected-已失败,相比传统回调函数更合理

所谓Promise ,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise是一个对象,从它能够获取异步操做的消息。 
Promise 对象的状态不受外界影响

古人云:“君子一言既出;驷马难追”,这种“承诺未来会执行”的对象在JavaScript中称为Promise对象。

 

postMessage(iframe间的跨域通讯)

http://www.javashuo.com/article/p-rvabgadb-bs.html

http://www.webhek.com/post/postmessage-cross-domain-post.html

HTML5给咱们带来了安全的跨域通讯接口,即window.postMessage()方法。

它方法原型是:window.postMessage(msg, domain),咱们能够给指定的domain发送msg。而接收msg的iframe只要注册一个监听事件就能够了。

相关文章
相关标签/搜索