基本数据类型:javascript
number、html
string、前端
boolean、java
null、git
undefined 引用数据类型: object(普通对象、数组对象、正则对象、日期对象、Math、实例、 prototype等)github
function(普通函数和类) 特殊数据类型:Symbolweb
基本数据类型按值操做,引用数据类型按照堆内存的引用地址来操做面试
数据类型检测四种方式: typeof编程
instanceofjson
constructor
Object.prototype.toString.call()
webkit、
Gecko、
Trident、
Presto等
函数执行会造成一个私有的做用域(私有栈内存),这就是闭包,在真实项目 中主要应用闭包的两大做用 保护
保存 以前研究过像JQUERY等类库的源码,为了防止全局变量污染,基本上全部代码 都是放到闭包中保护起来的 项目中遇到循环事件绑定,也会基于闭包保存的做用,去存储对应的索引 以前研究过JS高阶编程技巧:柯理化函数编程思想,这个就是闭包的应用,重 写过debounce、throttle函数节流和防抖的方法 ...... 由于闭包会产生不释放的栈内存,因此尽量在项目中少使用
面向对象是一种编程思想,js自己就是基于面向对象构建出来的(例如:js中有不少内置类,像Promise就是ES6中新增的一个内置类,咱们能够基于new Promise来建立一个实例,来管理异步编程,咱们在项目中Promise也常常用,本身也研究过他的源码。。。)咱们平时用的VUE/REACT/JQUERY也是基于面向对象构建出来的,他们都是类,平时开发的时候都是建立他们的额实例来操做的;固然我本身在真实项目中,也封装过一些组件插件,(例如:DIALOG、拖拽、、、)也是基于面向对象开发的,这样能够创造不一样的实例,来管理私有的属性和公有的方法,很方便。。。。
JS中的面向对对象,和其余编程语言还略有不一样,JS中类和实例都是基于原型和原型链机制来处理的; 并且js中关于类的重载、重写、继承也和其余语言不太同样
// 这个题我知道,我以前看过部分JQ源码
// 1.$(document).ready() 采用的是DOM2事件绑定,监听的是DOMContentLoaded这个事件,因此只要DOM结构加载完成就会被触发执行,并且同一个页面中可使用屡次(绑定不一样的方法,由于基于DOM2事件池绑定机制完成的) // 2.window.onload必须等待全部资源都加载完成才会被触发执行,采用DOM0事件绑定,同一个页面只能绑定一次(一个方法),想绑定多个也须要改成window.addEventListener('load', function () {})DOM2绑定方式
let 和 var 的区别
- 不存在变量提高
- 不容许重复声明
- 在全局做用域下设置变量不会给window设置属性
- 存在块级做用域
- 解决了一些暂时性死区问题
let 和 const 的区别 const 建立的是常量,存储的值不能被修改(准确说是不能修改变量的指 向)
call 和 apply 的区别 给方法传参的时候,call是按顺序,一个个传递;apply是把传递的参数放 到一个数组中传递;
在传递参数较多的状况下, call的性能要高于apply; call 和 bind 的区别 call在改变函数this指向的时候,会把函数当即执行,而bind不会把函数立 即执行,只是预先处理了this和实参信息;
真实项目中,咱们须要改变this指向的时候,会应用这三个方法,例如: 给元素进行事件绑定,咱们须要把事件触发,所执行的函数中this和参数进 行预先处理,此时可使用bind进行处理;
咱们能够基于call方法,让类数组借用数组原型上的方法,例如:把类数组 转换为数组
能够基于apply传参是一个数组,借用Math.max获取数组中的最大值 ......
forEach、
map、
find、
some、
filter、
reduce、
every、
sort等
// 开发者A
let AModule = (function () {
let n = 10;
let query = function () {
//...
};
let fn = function () {
//...
//调取开发者B编写的QUERY方法
BModule.query();
};
return {
query: query,
init: function () {
query();
fn();
}
}
})();
// 开发者B
let BModule = (function () {
let n = 20;
let query = function () {
//...
};
let sum = function () {
//...
//调取开发者A编写的QUERY方法
AModule.query();
};
return {
query: query,
init: function () {
query();
sum();
}
}
})();
AModule.init();
BModule.init();
复制代码
var r2 = typeof typeof typeof sum; // "string"
执行顺序:先执行最后一个 typeof sum -> "function" -> typeof typeof "function" -> typeof "string" -> "string"
只要是两个及以上的typeof 最后返回结果就是 "string"
第一次握手:创建链接。客户端发送链接请求报文段,将SYN位置为1,Sequence Number为x;而后,客户端进入SYN_SEND状态,等待服务器的确认;
第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,须要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,本身本身还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述全部信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK报文段。而后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕之后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
完成了三次握手,客户端和服务器端就能够开始传送数据。
ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0。
TCP协议规定,只有ACK=1时有效,也规定链接创建后全部发送的报文的ACK必须为1。
SYN(SYNchronization) : 在链接创建时用来同步序号。当SYN=1而ACK=0时,代表这是一个链接请求报文。对方若赞成创建链接,则应在响应报文中使SYN=1和ACK=1. 所以, SYN置1就表示这是一个链接请求或链接接受报文。 FIN (finis)即完,终结的意思, 用来释放一个链接。当 FIN = 1 时,代表此报文段的发送方的数据已经发送完毕,并要求释放链接。
发送HTTP请求,服务器处理请求,返回响应结果 TCP链接创建后,浏览器就能够利用HTTP/HTTPS协议向服务器发送请求了。服务器接受到请求,就解析请求头,若是头部有缓存相关信息如if-none-match与if-modified-since,则验证缓存是否有效,如有效则返回状态码为304,若无效则从新返回资源,状态码为200. 关闭TCP链接
第一次分手:主机1(可使客户端,也能够是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“赞成”你的关闭请求;
第三次分手:主机2向主机1发送FIN报文段,请求关闭链接,同时主机2进入LAST_ACK状态;
第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,而后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段之后,就关闭链接;此时,主机1等待2MSL后依然没有收到回复,则证实Server端已正常关闭,那好,主机1也能够关闭链接了。
小提示: 若是日常自身有使用场景可结合使用场景进行讲解,好比我在这里使用过的场景是CORS和Nginx反向代理。
小提示:若是你提到JSONP,面试官确定会问你整个详细的实现过程,因此必定要搞懂JSONP的实现原理,若是不是很理解能够本身起一个Express服务实践一下。
Web前端事先定义一个用于获取跨域响应数据的回调函数,并经过没有同源策略限制的script标签发起一个请求(将回调函数的名称放到这个请求的query参数里),而后服务端返回这个回调函数的执行,并将须要响应的数据放到回调函数的参数里,前端的script标签请求到这个执行的回调函数后会立马执行,因而就拿到了执行的响应数据。
缺点: JSONP只能发起GET请求
这里给出几个连接:
segmentfault.com/a/119000001… zhangguixu.github.io/2016/12/02/… www.cnblogs.com/iovec/p/531…
前端构造一个恶意页面,请求JSONP接口,收集服务端的敏感信息。若是JSONP接口还涉及一些敏感操做或信息(好比登陆、删除等操做),那就更不安全了。
解决方法:验证JSONP的调用来源(Referer),服务端判断Referer是不是白名单,或者部署随机Token来防护。
不严谨的 content-type致使的 XSS 漏洞,想象一下 JSONP 就是你请求 http://youdomain.com?callback=douniwan
, 而后返回 douniwan({ data })
,那假如请求 http://youdomain.com?callback=<script>alert(1)</script>
不就返回 <script>alert(1)</script>({ data })
了吗,若是没有严格定义好 Content-Type( Content-Type: application/json ),再加上没有过滤 callback
参数,直接当 html 解析了,就是一个赤裸裸的 XSS 了。
解决方法:严格定义 Content-Type: application/json,而后严格过滤 callback
后的参数而且限制长度(进行字符转义,例如<换成<,>换成>)等,这样返回的脚本内容会变成文本格式,脚本将不会执行。
能够将执行的代码转发到服务端进行校验JSONP内容校验,再返回校验结果。
小提示:若是你回答跨域解决方案CORS,那么面试官必定会问你实现CORS的响应头信息Access-Control-Allow-Origin。
CORS(跨域资源共享 Cross-origin resource sharing)容许浏览器向跨域服务器发出XMLHttpRequest请求,从而克服跨域问题,它须要浏览器和服务器的同时支持。
请求方法是如下三种方法之一:
HTTP的请求头信息不超出如下几种字段:
后端的响应头信息:
非简单请求是那种对服务器有特殊要求的请求,好比请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通讯以前,增长一次HTTP查询请求,称为"预检"请求(preflight)。
若是浏览器否认了"预检"请求,会返回一个正常的HTTP回应,可是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不一样意预检请求,所以触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。
Nginx反向代理
postMessage
document.domain
function Dog(name) {
this.name = name;
}
Dog.prototype={
bark(){
console.log('wangwang');
},
sayName(){
console.log('my name is ' + this.name);
}
};
function _new() {
//=>完成你的代码
}
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true
================================================
//——> 重写 _new
function _new(Fn, ...arg) {
let obj = {};
obj.__proto__ = Fn.prototype;
Fn.call(obj, ...arg);
return obj;
}
复制代码
/* 实现一个$attr(name,value)遍历 * 属性为name * 值为value的元素集合 * 例以下面示例: */
let ary = $attr('class','box'); //=>获取页面中全部class为box的元素
=====================================
function $attr(property, value) {
let elements = document.getElementsByTagName('*'),
arr = [];
elements = Array.from(elements);
elements.forEach(item => {
let itemValue = item.getAttribute(property);
if (property==='class') {
new RegExp("\\b" + value +
"\\b").test(itemValue)?arr.push(item):null;
return;
}
if (itemValue === value) {
arr.push(item);
}
});
return arr;
}
console.log($attr('class', 'box'));
复制代码
~function(){
//=>bind方法在IE6~8中不兼容,接下来咱们本身基于原生JS实现这个方法
function bind(){
};
Function.prototype.bind=bind;
}();
var obj = {name:'zhufeng'};
function func(){
console.log(this,arguments);
//=>当点击BODY的时候,执行func方法,输出:obj
[100,200,MouseEvent事件对象]
}
document.body.onclick = func.bind(obj,100,200);
=============================================
~function(){
//=>bind方法在IE6~8中不兼容,接下来咱们本身基于原生JS实现这个方法
function bind(context){
context=context||window;
var _this = this,
outerArg=[].slice.call(arguments,1);
return function anonymous() {
var innerArg=[].slice.call(arguments,0);
_this.apply(context, outerArg.concat(innerArg));
}
};
Function.prototype.bind=bind;
}();
复制代码
function Modal(x,y){
this.x=x;
this.y=y;
}
Modal.prototype.z=10;
Modal.prototype.getX=function(){
console.log(this.x);
}
Modal.prototype.getY=function(){
console.log(this.y);
}
Modal.n=200;
Modal.setNumber=function(n){
this.n=n;
};
let m = new Model(10,20);
===============================================
class Modal{
constructor(x,y){
this.x=x;
this.y=y;
}
getX(){
console.log(this.x);
}
getY(){
console.log(this.y);
}
static setNumber(n){
this.n=n;
}
}
Modal.n=200;
Modal.prototype.z=10;
复制代码
~function(){
function changeThis(context){
context=context||window;
//let arg=[].slice.call(arguments,1);
let arg=[],
_this=this,
result=null;
for(let i=1;i<arguments.length;i++){
arg.push(arguments[i]);
}
context.$fn=_this;
result=context.$fn(...arg);
delete context.$fn;
return result;
};
Function.prototype.changeThis=changeThis;
}();
let obj = {name:'Alibaba',$fn:100};
function fn(x,y){
this.total=x+y;
return this;
}
let res = fn.changeThis(obj,100,200);
//res => {name:'Alibaba',total:300}
复制代码
~function(){
/*生成随机函数名:时间戳的方式*/
function queryRandomName(){
let time=new Date().getTime();
return '$zhufeng'+time;
}
/*模拟CALL方法改变函数中的THIS*/
function changeThis(context=window,arg=[]){
let _this=this,
result=null,
ran=queryRandomName();
context[ran]=_this;
result=context[ran](...arg);
delete context[ran];
return result;
};
Function.prototype.changeThis=changeThis;
}();
let res = fn.changeThis(obj,[100,200]);
复制代码
/* 已知宽高 */
.box {
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
width: 100px;
height: 100px;
}
========================
/* 未知宽高 */
.box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
==========================
.box {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
===============================
.container {
display: flex;
justify-content: center;
align-items: center;
}
.container .box {}
复制代码
/*
* 1.编写一个方法“flatten”,将数组扁平化 (至少两种办法)
* 2.而后实现“unique”数组去重方法,把数组进行去重 (至少两种办法)
*/
let arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9,[11, 12, [12,
13, [14]]]], 10];
ary.flatten().unique().sort((a,b)=>a-b); //=>[1, 2, 3, 4, 5,
6, 7, 8, 9....]
============================================
~function(){
function flatten(){
/*第一种*/
return JSON.stringify(this).replace(/(\
[|\])/g,'').split(',').map(item=>{
return Number(item);
});
/*第二种*/
return this.flat(Infinity);
/*第三种*/
return this.toString().split(',').map(item=>{
return Number(item);
});
/*第四种*/
let arr=this.slice(0);
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
/*第五种*/
let result = [];
let fn = function (ary) {
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
if (Array.isArray(ary[i])) {
fn(item);
} else {
result.push(item);
}
}
}
fn(this);
return result;
}
function unique(){
/*第一种*/
return Array.from(new Set(this));
/*第二种*/
return [...new Set(this)];
/*第三种*/
let obj={};
for(let i=0;i<this.length;i++){
let item=this[i];
if(typeof obj[item]!=='undefined'){
this[i]=this[this.length-1];
this.length--;
i--;
continue;
}
obj[item]=item;
}
return this;
}
Array.prototype.flatten=flatten;
Array.prototype.unique=unique;
}();
let arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9,[11, 12, [12,
13, [14]]]], 10];
ary.flatten().unique().sort((a,b)=>a-b); //=>[1, 2, 3, 4, 5,
6, 7, 8, 9....]
复制代码