前端面试总结

一、js基础

  • 1.匿名函数优点

  1. 匿名函数可以有效的保证在页面上写入Javascript,而不会造成全局变量的污染。
  2. 声明即可执行。
  3. 执行完内存就会释放。
  • 2.事件冒泡、事件捕获、事件代理、事件绑定、

事件冒泡:事件从深到浅的过程,从最深元素到根元素的过程,称谓冒泡。

事件捕获:事件从根元素到最深元素的过程查找传递的过程。

当捕获事件和冒泡事件同时存在的时候,冒泡事件的优先级会高一些。addEventListener方法第三个参数默认为false,就是默认在冒泡阶段执行。

事件代理:事件委托是利用事件的冒泡原理来实现的,在父元素上注入事件作为代理,当事件触发时,通过target属性来确定当前具体是哪个元素触发的,减少事件注入的次数,提高性能。

// 例
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);
    }
  }
}

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

不适合用事件委托的事件:mousemove(每次要计算它的位置,不好把控)blur,focus(没有冒泡特性)
扩展:Ext框架里面Ext.EventManager这个类来实现,

jQuery:1.7通过on,off来实现事件绑定。

事件绑定的几种方式:

    A:直接在html中定义事件

    B:使用js直接定义

    C:addEventListener实现监听

如果同时使用这3种方式注入同一个事件,比如click,B会将A覆盖,事件B、C会执行,且B先与C;后续再以B的形式添加,B会覆盖,C则不会。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <h2>事件注入</h2>
  <div id="event_write" onclick="alert('html直接注入')">html直接注入</div>
  <div id="event_injection">函数等于注入</div>
  <div id="event_injection_add">addEventListener注入</div>
  <script>
    window.onload = function(){
      <!--测试优先级-->
      document.getElementById('event_write').onclick = function () {
        alert('write函数等于注入')
      };
      //    不会执行
      document.getElementById('event_write').onclick = function () {
        alert('write函数等于注入1')
      };
      <!--测试优先级-->
      document.getElementById('event_write').addEventListener('click', ()=> {
        alert('write addEventListener注入')
      });
      // 会执行
      document.getElementById('event_write').addEventListener('click', ()=> {
        alert('write addEventListener注入1')
      });

      document.getElementById('event_injection').onclick = function () {
        alert('函数等于注入')
      };

      document.getElementById('event_injection_add').addEventListener('click', ()=> {
        alert('addEventListener注入')
      });
    }
  </script>
</body>
</html>
  • 3.双向数据绑定

前端数据绑定实现

  • 4.跨域,你知道几种方法?具体说说jsonp

跨域:这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,
比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据。

只要协议、域名、端口有任何一个不同,都被当作是不同的域。

下表给出了相对http://store.company.com/dir/page.html同源检测的结果:

QQ截图20130613230631

1)、通过jsonp跨域

在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。

比如,有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设这个json数据地址是http://example.com/data.php,那么a.html中的代码就可以这样:

QQ截图20130613230631

我们看到获取数据的地址后面还有一个callback参数,按惯例是用这个参数名,但是你用其他的也一样。当然如果获取数据的jsonp地址页面不是你自己能控制的,就得按照提供数据的那一方的规定格式来操作了。

因为是当做一个js文件来引入的,所以http://example.com/data.php返回的必须是一个能执行的js文件,所以这个页面的php代码可能是这样的:

QQ截图20130613230631

最终那个页面输出的结果是:

QQ截图20130613230631

所以通过http://example.com/data.php?callback=dosomething得到的js文件,就是我们之前定义的dosomething函数,并且它的参数就是我们需要的json数据,这样我们就跨域获得了我们需要的数据。

这样jsonp的原理就很清楚了,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。

知道jsonp跨域的原理后我们就可以用js动态生成script标签来进行跨域操作了,而不用特意的手动的书写那些script标签。如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了。

QQ截图20130613230631

原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。

2)、通过修改document.domain来跨子域

浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。有一点需要说明,不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但蛋疼的是你却不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器比如ie6也可以使用top、parent等少数几个属性),总之,你可以当做是只能获取到一个几乎无用的window对象。比如,有一个页面,它的地址是http://www.example.com/a.html  , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:

  QQ截图20130613230631

这个时候,document.domain就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。

在页面 http://www.example.com/a.html 中设置document.domain:

QQ截图20130613230631

在页面 http://example.com/b.html 中也设置document.domain,而且这也是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值:

QQ截图20130613230631

这样我们就可以通过js访问到iframe中的各种属性和对象了。

不过如果你想在http://www.example.com/a.html 页面中通过ajax直接请求http://example.com/b.html 页面,即使你设置了相同的document.domain也还是不行的,所以修改document.domain的方法只适用于不同子域的框架间的交互。如果你想通过ajax的方法去与不同子域的页面交互,除了使用jsonp的方法外,还可以用一个隐藏的iframe来做一个代理。原理就是让这个iframe载入一个与你想要通过ajax获取数据的目标页面处在相同的域的页面,所以这个iframe中的页面是可以正常使用ajax去获取你要的数据的,然后就是通过我们刚刚讲得修改document.domain的方法,让我们能通过js完全控制这个iframe,这样我们就可以让iframe去发送ajax请求,然后收到的数据我们也可以获得了。

3)、使用window.name来进行跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

比如:有一个页面a.html,它里面有这样的代码:

QQ截图20130613230631

再看看b.html页面的代码:

QQ截图20130613230631

a.html页面载入后3秒,跳转到了b.html页面,结果为:

QQ截图20130613230631

我们看到在b.html页面上成功获取到了它的上一个页面a.html给window.name设置的值。如果在之后所有载入的页面都没对window.name进行修改的话,那么所有这些页面获取到的window.name的值都是a.html页面设置的那个值。当然,如果有需要,其中的任何一个页面都可以对window.name的值进行修改。注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。

上面的例子中,我们用到的页面a.html和b.html是处于同一个域的,但是即使a.html与b.html处于不同的域中,上述结论同样是适用的,这也正是利用window.name进行跨域的原理。

下面就来看一看具体是怎么样通过window.name来跨域获取数据的。还是举例说明。

比如有一个www.example.com/a.html页面,需要通过a.html页面里的js来获取另一个位于不同域上的页面www.cnblogs.com/data.html里的数据。

data.html页面里的代码很简单,就是给当前的window.name设置一个a.html页面想要得到的数据值。data.html里的代码:

QQ截图20130613230631

那么在a.html页面中,我们怎么把data.html页面载入进来呢?显然我们不能直接在a.html页面中通过改变window.location来载入data.html页面,因为我们想要即使a.html页面不跳转也能得到data.html里的数据。答案就是在a.html页面中使用一个隐藏的iframe来充当一个中间人角色,由iframe去获取data.html的数据,然后a.html再去得到iframe获取到的数据。

充当中间人的iframe想要获取到data.html的通过window.name设置的数据,只需要把这个iframe的src设为www.cnblogs.com/data.html就行了。然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的window.name的值,还必须把这个iframe的src设成跟a.html页面同一个域才行,不然根据前面讲的同源策略,a.html是不能访问到iframe里的window.name属性的。这就是整个跨域过程。

看下a.html页面的代码:

上面的代码只是最简单的原理演示代码,你可以对使用js封装上面的过程,比如动态的创建iframe,动态的注册各种事件等等,当然为了安全,获取完数据后,还可以销毁作为代理的iframe。网上也有很多类似的现成代码,有兴趣的可以去找一下。

通过window.name来进行跨域,就是这样子的。

4)、使用HTML5中新引进的window.postMessage方法来跨域传送数据

window.postMessage(message,targetOrigin)  方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 *  。

需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。

上面所说的向其他window对象发送消息,其实就是指一个页面有几个框架的那种情况,因为每一个框架都有一个window对象。在讨论第二种方法的时候,我们说过,不同域的框架间是可以获取到对方的window对象的,而且也可以使用window.postMessage这个方法。下面看一个简单的示例,有两个页面

QQ截图20130613230631

QQ截图20130613230631

我们运行a页面后得到的结果:

QQ截图20130613230631

我们看到b页面成功的收到了消息。

使用postMessage来跨域传送数据还是比较直观和方便的,但是缺点是IE6、IE7不支持,所以用不用还得根据实际需要来决定。

  • 5.xss攻击

XSS攻击是Web攻击中最常见的攻击方法之一,它是通过对网页注入可执行代码且成功地被浏览器
执行,达到攻击的目的,形成了一次有效XSS攻击,一旦攻击成功,它可以获取用户的联系人列
表,然后向联系人发送虚假诈骗信息,可以删除用户的日志等等,有时候还和其他攻击方式同时实
施比如SQL注入攻击服务器和数据库、Click劫持、相对链接劫持等实施钓鱼,它带来的危害是巨

大的,是web安全的头号大敌。

1)一旦在DOM解析过程成出现不在预期内的改变(JS代码执行或样式大量变化时),就可能发生XSS攻击
2)XSS分为反射型XSS,存储型XSS和DOM XSS
3)反射型XSS是在将XSS代码放在URL中,将参数提交到服务器。服务器解析后响应,在响应结果中存在XSS代码,最终通过浏览器解析执行。
4)存储型XSS是将XSS代码存储到服务端(数据库、内存、文件系统等),在下次请求同一个页面时就不需要带上XSS代码了,而是从服务器读取。
5)DOM XSS的发生主要是在JS中使用eval造成的,所以应当避免使用eval语句。
6)XSS危害有盗取用户cookie,通过JS或CSS改变样式,DDos造成正常用户无法得到服务器响应。
7)XSS代码的预防主要通过对数据解码,再过滤掉危险标签、属性和事件等。

  • 6.说说let,const,var的区别?

1)var定义的变量可以修改,如果不初始化会输出undefined,不会报错。支持变量提升,并且一旦定义就挂载在window上,在性能上也有一定的劣势。并且一旦被定义,JavaScript会把作用域里的所有变量和函数提到函数的顶部声明,只是没有被初始化,并且变量提升指的是变量声明的提升,不会提升变量的初始化和赋值。

/*****************变量提升***************/
function test2() {
    console.log(c); // undefined
   var c = 2; // 提升到顶部
}


(function () {
    var a = b = 3;
})();

console.log(a); //  Uncaught ReferenceError: a is not defined
console.log(b); // 3
/*****************变量提升测试***************/
function test() {
	var obj = {};
			for (var i = 0; i < 5; i++) {
				var obj2 = {};
				console.log('直接调用', i); // 0 1 2 3 4
				obj.i = i;
				obj2.i = i;
				setTimeout(function () {
					// 无单独作用域
					console.log('内部', i); // 变量赋值为5,向上查找都是5 5 5 5 5 5
				}, 1000);
				// 有单独作用域
				test2(i, obj, obj2);
			}
		}

		/**
		 * @param i  值传递 参数开辟内存 打印 0 - 4
		 * @param obj // 引用类型 寻找引用地址 4 4 4 4 4
		 * @param obj2 // 初始化了 寻找引用地址 0 - 4
		 */
		 var test2 = function(i, obj, obj2) {
			setTimeout(function () {
				console.log('函数调用',i, obj.i, obj2.i)
			}, 1000)
		}

 通过new Image()创建后的onload函数必须等主进程走完后才会执行

function sleep(numberMillis) {
			var now = new Date();
			var exitTime = now.getTime() + numberMillis;
			while (true) {
				now = new Date();
				if (now.getTime() > exitTime)
					return;
			}
		}
		
		function _go() {
			var imgArr = ['test.png', 'colors.png', 'crate.gif', 'envmap.png', 'cm_gray.png'];
			for(var i = 0; i < imgArr.length ; i++){
				var img = new Image();
				img.src = './textures/' + imgArr[i];
//				img.onload = function () {
//					console.log('图片加载成功', i); // 打印都是5 主进程没有执行完,加载进程被阻塞
//				};
				setLoad(img, i);
				console.log('直接打印',i);
				sleep(1000);
			}
			console.log('主进程1')
			sleep(1000);
			console.log('主进程2')
		}

2)const定义的变量不可以修改,而且必须初始化。

3)let是块级作用域,函数内部使用let定义后,对函数外部无影响,出了这个块级就释放了。

  • 7.你知道cmd的amd的区别和用法吗?

AMD  requirejs  依赖前置 把依赖的文件优先加载,在回调定义自己的模块及业务
CMD  SesJs      依赖就近 在哪个地方用,在哪个地方加载。
CommonJs     node.js推荐使用的模块化开发规范,浏览器不支持
ES6             export/import

Array.prototype._forEach = function (fn) {
    const len = this.length;
    let  index = 0;
    for (let i = 0; i < len; i++) {
        // 过滤掉稀疏项
        if(i in this) {
            fn(this[i], index, this);
            index ++;
        }
    }
}
// 测试
[0,1,2,,, 3]._forEach((item, index)=> {
    console.log(item, index)
});

           提供iterator接口的数据类型(Array Set Map)可以使用for of遍历,普通对象没有内置该接口,如果想要for of可以循环对象,则需要给对象添加该属性

var testObj = {
    a: 1,
    b: 2,
    c: 3
}
// ES5
Object.defineProperty(testObj, Symbol.iterator, {
    enumerable: false, // 不可遍历
    writeable: false, // 不可修改改属性
    configurable: true, // 重新定义属性
    value: function () {
        var index = 0;
        var me = this;
        var o = Object.keys(me);
        return {
            next: function () {
                return {
                    value: me[o[index++]],
                    done: index > o.length
                }
            },
        }
    }
});

// ES6实现
Object.defineProperty(testObj, Symbol.iterator, {
    enumerable: false, // 不可遍历
    writeable: false, // 不可修改改属性
    configurable: true, // 重新定义属性
    value: function* () { // Generator 函数
        const o = Object.keys(this);
        for(let i = 0, l = o.length; i < l; i++){
            yield this[o[i]];
        }
    }
});
// 测试
for(var key of testObj) {
    console.log(key)
}
  • 15、rxjs运用场景及优势

           连接

js中调用函数可以分为3种吧

一种全局定义,此时this的指向为window

var name = "window name";
var WinNameTest = function() {
	console.log(this.name); // window name
}();

var ThisNameTest = function() {
	this.name = "ThisNameTest";
	console.log(this.name); // ThisNameTest
}();

一种是对象里面一个属性为函数,此时this的指向为谁调用指向谁

var objNameTest = {
	name: "objNameTest",
	getName: function() {
		console.log(this.name); // objNameTest
	}
};
objNameTest.getName();

var ChangeThisTest = {
	name: "ChangeThisTest",
	getName: objNameTest.getName 
};

ChangeThisTest.getName(); // ChangeThisTest

还有一种是通过new去实例化后调用,此时this指向的是这个实例,可以通过call apply bind改变这个实例。 

var newNameTest = function() {
	this.name = "newNameTest";
	console.log(this.name); // newNameTest
}

new newNameTest();


var ThisNameTest = function() {
	this.name = "ThisNameTest";
	console.log(this.name); // ThisNameTest
}();

var newNameTest = function() {
	console.log(this.name); // ThisNameTest
}
newNameTest.call(ThisNameTest);
  • 18 实现进制转换函数

面试的时候没想到用toString去实现,现在接着用当前的方式实现下

      const hex = (val = 0, hexV = 2) => {
        /**
         * 保存进制的值
         * @type {Array}
         */
        const hexA = [];
        /**
         * 实现思路 一直求值取余下去 如果有余数 则该位数是余数
         * 否则接着往下递归 直到比hexV小
         * @param v
         * @returns {Array}
         */
        const fn = (v) => {
          if (v < hexV) {
            hexA.unshift(v);
            return hexA
          }
          const bit = v % hexV;
          hexA.unshift(bit);
          /**
           * 传递下去的必须是正整数
           */
          return fn(parseInt(v / hexV))
        };
        return fn(val);
      };
      FlyweightHex.getDom().innerHTML = '二进制: ' + hex(10, 2) + ' --- toString --- ' + (10).toString(2);
      FlyweightHex.getDom().innerHTML = '八进制: ' + hex(36, 8) + ' --- toString --- ' + (36).toString(8);

二、CSS

  • 1、模块居中,如何做一个弹窗并且居中

链接

  • 2、说说定位position属性

描述
absolute

生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。

元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。

fixed

生成绝对定位的元素,相对于浏览器窗口进行定位。

元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。

relative

生成相对定位的元素,相对于其正常位置进行定位。

因此,"left:20" 会向元素的 LEFT 位置添加 20 像素。

static默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。
inherit规定应该从父元素继承 position 属性的值。

三、HTML 

HTML总结

四、性能优化

  •  1、静态资源加载

       1)ionic图片可以组合成一张 或者使用伪类
       2)js可以异步加载、如果首页加载过多,可以考虑合并一些,后续就按需加载

  •   2、技术细节上

  1. 可视范围内加载        
  2. 一些即时通讯使用webSoccket
  3. 一些细节上做好去抖
  4. 全局变量做好限制,如对var使用的限制
  5. 大的计算量可以考虑使用webworker去做运算
  6. 页面缓存,对于常用的页面可以做缓存处理(部分缓存)
  7. 一些可视化的东西 用好离屏和分层

五、网络

  • 1、说说对DNS的理解

域名系统(Domain Name System缩写DNS,Domain Name被译为域名)是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。

链接

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值