手写路径导航javascript
new
操做符new
操做符作了这些事:css
[[Prototype]]
(也就是__proto__
)连接。this
指向新建立的对象。。new
建立的每一个对象将最终被[[Prototype]]
连接到这个函数的prototype
对象上。Object
(包含Functoin, Array, Date, RegExg, Error
),那么new
表达式中的函数调用将返回该对象引用。function New(func) {
var res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);
复制代码
JSON.stringify
JSON.stringify(value[, replacer [, space]])
:html
Boolean | Number| String
类型会自动转换成对应的原始值。undefined
、任意函数以及symbol
,会被忽略(出如今非数组对象的属性值中时),或者被转换成 null
(出如今数组中时)。function jsonStringify(obj) {
lettype = typeof obj;
if (type !== "object" || type === null) {
if (/string|undefined|function/.test(type)) {
obj = '"' + obj + '"';
}
return String(obj);
} else {
let json = []
arr = (obj && obj.constructor === Array);
for (let k in obj) {
let v = obj[k];
lettype = typeof v;
if (/string|undefined|function/.test(type)) {
v = '"' + v + '"';
} elseif (type === "object") {
v = jsonStringify(v);
}
json.push((arr ? "" : '"' + k + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
}
}
jsonStringify({x : 5}) // "{"x":5}"
jsonStringify([1, "false", false]) // "[1,"false",false]"
jsonStringify({b: undefined}) // "{"b":"undefined"}"
复制代码
JSON.parse
JSON.parse(text[, reviver])
前端
用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。提供可选的reviver函数用以在返回以前对所获得的对象执行变换(操做)。vue
function jsonParse(opt) {
returneval('(' + opt + ')');
}
jsonParse(jsonStringify({x : 5}))
// Object { x: 5}
jsonParse(jsonStringify([1, "false", false]))
// [1, "false", falsr]
jsonParse(jsonStringify({b: undefined}))
// Object { b: "undefined"}
复制代码
避免在没必要要的状况下使用
eval
,eval() 是一个危险的函数, 他执行的代码拥有着执行者的权利。若是你用 eval()运行的字符串代码被恶意方(不怀好意的人)操控修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。java
它会执行JS代码,有XSS漏洞。react
若是你只想记这个方法,就得对参数json作校验。webpack
var rx_one = /^[\],:{}\s]*$/;
var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
if (
rx_one.test(
json
.replace(rx_two, "@")
.replace(rx_three, "]")
.replace(rx_four, "")
)
) {
var obj = eval("(" +json + ")");
}
复制代码
来源 神奇的eval()与new Function()git
核心:Function
与eval
有相同的字符串参数特性。
var func = new Function(arg1, arg2, ..., functionBody);
在转换JSON的实际应用中,只须要这么作。
var jsonStr = '{ "age": 20, "name": "jack" }'
var json = (new Function('return ' + jsonStr))();
复制代码
eval
与 Function
都有着动态编译js代码的做用,可是在实际的编程中并不推荐使用。
这里是面向面试编程,写这两种就够了。至于第三,第四种,涉及到繁琐的递归和状态机相关原理,具体能够看:
call
或 apply
call
语法:
fun.call(thisArg, arg1, arg2, ...)
,调用一个函数, 其具备一个指定的this值和分别地提供的参数(参数的列表)。
apply
语法:
func.apply(thisArg, [argsArray])
,调用一个函数,以及做为一个数组(或相似数组对象)提供的参数。
Function.call
按套路实现call
核心:
this
到函数并传入给定参数执行函数为啥说是套路实现呢?由于真实面试中,面试官很喜欢让你逐步地往深考虑,这时候你能够反套路他,先写个简单版的:
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
}
foo.bar() // 1
复制代码
当面试官有进一步的发问,或者此时你能够伪装思考一下。而后写出如下版本:
Function.prototype.call2 = function(content = window) {
content.fn = this;
let args = [...arguments].slice(1);
let result = content.fn(...args);
delect content.fn;
return result;
}
var foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1
复制代码
Function.apply
的模拟实现apply()
的实现和call()
相似,只是参数形式不一样。直接贴代码吧:
Function.prototype.apply2 = function(context = window) {
context.fn = this
let result;
// 判断是否有第二个参数
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn()
return result
}
复制代码
Function.bind()
bind()
方法:
会建立一个新函数。当这个新函数被调用时,bind() 的第一个参数将做为它运行时的 this,以后的一序列参数将会在传递的实参前传入做为它的参数。(来自于 MDN )
此外,bind
实现须要考虑实例化后对原型链的影响。
Function.prototype.bind2 = function(content) {
if(typeof this != "function") {
throw Error("not a function")
}
// 若没问参数类型则从这开始写
let fn = this;
let args = [...arguments].slice(1);
let resFn = function() {
return fn.apply(this instanceof resFn ? this : content,args.concat(...arguments) )
}
functiontmp() {}
tmp.prototype = this.prototype;
resFn.prototype = new tmp();
return resFn;
}
复制代码
寄生组合式继承
通常只建议写这种,由于其它方式的继承会在一次实例中调用两次父类的构造函数或有其它缺点。
核心实现是:用一个 F
空的构造函数去取代执行了 Parent
这个构造函数。
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
function Child(name, parentName) {
Parent.call(this, parentName);
this.name = name;
}
function create(proto) {
functionF(){}
F.prototype = proto;
return new F();
}
Child.prototype = create(Parent.prototype);
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
Child.prototype.constructor = Child;
var parent = new Parent('father');
parent.sayName(); // parent name: father
var child = new Child('son', 'father');
复制代码
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数且返回结果的新函数的技术。
函数柯里化的主要做用和特色就是参数复用、提早返回和延迟执行。
functioncurry() {
var args = Array.prototype.slice.call(arguments);
var fn = function() {
var newArgs = args.concat(Array.prototype.slice.call(arguments));
return multi.apply(this, newArgs);
}
fn.toString = function() {
return args.reduce(function(a, b) {
return a * b;
})
}
return fn;
}
function multiFn(a, b, c) {
return a * b * c;
}
var multi = curry(multiFn);
multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);
复制代码
ES6
骚写法const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args])
let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) //返回10
curryTest(1,2)(4)(3) //返回10
curryTest(1,2)(3,4) //返回10
复制代码
Promise
(中高级必考)咱们来过一遍Promise/A+
规范:
pending| fulfilled(resolved) | rejected
pending
状态的时候,能够转移到fulfilled(resolved)
或者rejected
状态fulfilled(resolved)
状态或者rejected
状态的时候,就不可变。then
异步执行方法,then
接受两个参数且必须返回一个promise:// onFulfilled 用来接收promise成功的值
// onRejected 用来接收promise失败的缘由
promise1=promise.then(onFulfilled, onRejected);
复制代码
Promise
的流程图分析Promise
用法:
var promise = new Promise((resolve,reject) => {
if (操做成功) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function (value) {
// success
},function (value) {
// failure
})
复制代码
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
复制代码
同时,须要在myPromise
的原型上定义链式调用的then
方法:
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case"resolved":
onFullfilled(self.value);
break;
case"rejected":
onRejected(self.reason);
break;
default:
}
}
复制代码
测试一下:
var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//输出1
复制代码
直接贴出来吧,这个版本还算好理解
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(excutor) {
let that = this; // 缓存当前promise实例对象
that.status = PENDING; // 初始状态
that.value = undefined; // fulfilled状态时 返回的信息
that.reason = undefined; // rejected状态时 拒绝的缘由
that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
function resolve(value) { // value成功态时接收的终值
if(value instanceof Promise) {
return value.then(resolve, reject);
}
// 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环以后的新执行栈中执行。
setTimeout(() => {
// 调用resolve 回调对应onFulfilled函数
if (that.status === PENDING) {
// 只能由pending状态 => fulfilled状态 (避免调用屡次resolve reject)
that.status = FULFILLED;
that.value = value;
that.onFulfilledCallbacks.forEach(cb => cb(that.value));
}
});
}
function reject(reason) { // reason失败态时接收的拒因
setTimeout(() => {
// 调用reject 回调对应onRejected函数
if (that.status === PENDING) {
// 只能由pending状态 => rejected状态 (避免调用屡次resolve reject)
that.status = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach(cb => cb(that.reason));
}
});
}
// 捕获在excutor执行器中抛出的异常
// new Promise((resolve, reject) => {
// throw new Error('error in excutor')
// })
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
let newPromise;
// 处理参数默认值 保证参数后续可以继续执行
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected =
typeof onRejected === "function" ? onRejected : reason => {
throw reason;
};
if (that.status === FULFILLED) { // 成功态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try{
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
} catch(e) {
reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
});
})
}
if (that.status === REJECTED) { // 失败态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(that.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if (that.status === PENDING) { // 等待态
// 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
return newPromise = new Promise((resolve, reject) => {
that.onFulfilledCallbacks.push((value) => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
that.onRejectedCallbacks.push((reason) => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
};
复制代码
emmm,我仍是乖乖地写回进阶版吧。
Debouncing
)和节流(Throttling
)
scroll
事件自己会触发页面的从新渲染,同时scroll
事件的handler
又会被高频度的触发, 所以事件的handler
内部不该该有复杂操做,例如DOM
操做就不该该放在事件处理中。 针对此类高频度触发事件问题(例如页面scroll
,屏幕resize
,监听用户输入等),有两种经常使用的解决方法,防抖和节流。
Debouncing
)实现典型例子:限制 鼠标连击 触发。
一个比较好的解释是:
当一次事件发生后,事件处理器要等必定阈值的时间,若是这段时间过去后 再也没有 事件发生,就处理最后一次发生的事件。假设还差
0.01
秒就到达指定时间,这时又来了一个事件,那么以前的等待做废,须要从新再等待指定时间。
// 防抖动函数
function debounce(fn,wait=50,immediate) {
let timer;
returnfunction() {
if(immediate) {
fn.apply(this,arguments)
}
if(timer) clearTimeout(timer)
timer = setTimeout(()=> {
fn.apply(this,arguments)
},wait)
}
}
复制代码
Throttling
)实现能够理解为事件在一个管道中传输,加上这个节流阀之后,事件的流速就会减慢。实际上这个函数的做用就是如此,它能够将一个函数的调用频率限制在必定阈值内,例如 1s,那么 1s 内这个函数必定不会被调用两次
function throttle(fn, wait) {
let prev = new Date();
returnfunction() {
const args = arguments;
const now = new Date();
if (now - prev > wait) {
fn.apply(this, args);
prev = new Date();
}
}
复制代码
经过第三个参数来切换模式。
const throttle = function(fn, delay, isDebounce) {
let timer
let lastCall = 0
returnfunction (...args) {
if (isDebounce) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn(...args)
}, delay)
} else {
const now = new Date().getTime()
if (now - lastCall < delay) return
lastCall = now
fn(...args)
}
}
}
复制代码
有个最著名的乞丐版实现,在《你不知道的JavaScript(上)》里也有说起:
var newObj = JSON.parse( JSON.stringify( someObj ) );
复制代码
### 10.2 面试够用版
function deepCopy(obj){
//判断是不是简单数据类型,
if(typeof obj == "object"){
//复杂数据类型
var result = obj.constructor == Array ? [] : {};
for(let i in obj){
result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
}
}else {
//简单数据类型 直接 == 赋值
var result = obj;
}
return result;
}
复制代码
关于深拷贝的讨论每天有,这里就贴两种吧,毕竟我...
instanceOf
function instanceOf(left,right) {
let proto = left.__proto__;
let prototype = right.prototype
while(true) {
if(proto === null) returnfalseif(proto === prototype) returntrue
proto = proto.__proto__;
}
}
复制代码
(两侧定宽,中间自适应)
采用了 absolute,致使父元素脱离了文档流,那全部的子元素也须要脱离文档流。若是页面复杂,那开发的难度可想而知
利用浮动 当中间内容高于两侧时,两侧高度不会随中间内容变高而变高
弹性盒子布局
利用负边距和浮动,实现起来比较复杂
利用网格布局
.container { display: grid; grid-template-columns: 100px auto 200px; } 复制代码
[...new Set(arr]
复制代码
var arr = [1,2,1,2,3,5,4,5,3,4,4,4,4],
init=[]
var result = arr.sort().reduce((init, current)=>{
console.log(init,current)
if(init.length===0 || init[init.length-1]!==current){
init.push(current);
}
return init;
}, []);
console.log(result);//1,2,3,4,5复制代码
复制代码
var deBounce=function(fn,wait=300){
let timer
returnfunction(){
if(timer){
clearTimeOut(timer)
}
timer=setTimeOut(()=>{
fn.apply(this,arguments)
},wait)
}
}
var throttle=function(fn,wait=300){
let prev=+newDate();
returnfunction(){
const args=argument,
now=+newDate();
if(now>last+wait){
last=now;
fn.apply(this,args)
}
}
}
复制代码
复制代码
//0 pending , 1 resolve,2 reject functionPromise(fn) {
...
this._state = 0// 状态标记
doResolve(fn, this)
}
functiondoResolve(fn, self) {
var done = false// 保证只执行一个监听try {
fn(function(value) {
if (done) return
done = true
resolve(self, value)
},
function(reason) {
if (done) return;
done = true
reject(self, value)
})
} catch(err) {
if (done) return
done = true
reject(self, err)
}
}
functionresolve(self, newValue) {
try {
self._state = 1;
...
}
catch(err) {
reject(self, err)
}
}
functionreject(self, newValue) {
self._state = 2;
...
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
}
复制代码
复制代码
functioncommafy(num) {
return num && num
.toString()
.replace(/(\d)(?=(\d{3})+\.)/g, function($0, $1) {
return $1 + ",";
});
}
console.log(commafy(1312567.903000))
复制代码
复制代码
javascript是单线程语言,任务设计成了两类,同步任务和异步任务 同步和异步任务分别进入不一样的执行“场所”,同步进入主线程,异步进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,回去了Event Queue读取对应的函数,进入主线程。 上述过程会不断重复,也就是常说的Event Loop(事件循环)。 可是,JS异步还有一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入event queue,而后再执行微任务,将微任务放入eventqueue,可是,这两个queue不是一个queue。当你往外拿的时候先从微任务里拿这个回调函数,而后再从宏任务的queue拿宏任务的回调函数 宏任务通常包括:总体代码script,setTimeout,setInterval。 微任务:Promise,process.nextTick
functioncreate(){
//1.建立一个空对象let obj={}
//2.获取构造函数let Con=[].shift.call(arguments)
//3.设置空对象的原型
obj._proto_=Con.prototype
//4.绑定this并执行构造函数,给新对象添加属性和方法let result=Con.apply(obj,arguments)
//5.确保返回值为对象return result instanceofObject?result:obj
}
复制代码
复制代码
/* 封装ajax函数
* @param {string}opt.type http链接的方式,包括POST和GET两种方式
* @param {string}opt.url 发送请求的url
* @param {boolean}opt.async 是否为异步请求,true为异步的,false为同步的
* @param {object}opt.data 发送的参数,格式为对象类型
* @param {function}opt.success ajax发送并接收成功调用的回调函数
*/functionmyAjax(opt){
opt = opt || {};
opt.method = opt.method.toUpperCase() || 'POST';
opt.url = opt.url || '';
opt.async = opt.async || true;
opt.data = opt.data || null;
opt.success = opt.success || function () {}
let xmlHttp = null;
if (XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}else{
xmlHttp =new ActiveXObject('Microsoft.XMLHTTP')
}
let params;
for (var key in opt.data){
params.push(key + '=' + opt.data[key]);
}
let postData = params.join('&');
if (opt.method.toUpperCase() === 'POST') {
xmlHttp.open(opt.method, opt.url, opt.async);
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
xmlHttp.send(postData);
}elseif (opt.method.toUpperCase() === 'GET') {
xmlHttp.open(opt.method, opt.url + '?' + postData, opt.async);
xmlHttp.send(null);
}
xmlHttp.onreadystatechange= function () {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
opt.success(xmlHttp.responseText);//若是是json数据能够在这使用opt.success(JSON.parse( xmlHttp.responseText))
}
};
}
复制代码
复制代码
var url = "http://www.taobao.com/index.php?key0=0&key1=1&key2=2";
functionparseQueryString(url){
var str = url.split("?")[1], //经过?获得一个数组,取?后面的参数
items = str.split("&"); //分割成数组var arr,name,value;
for(var i=0; i<items.length; i++){
arr = items[i].split("="); //["key0", "0"]
name = arr[0];
value = arr[1];
this[name] = value;
}
}
var obj = new parseQueryString(url);
alert(obj.key2)
复制代码
复制代码
HTTP协议(超文本传输协议) 主要特色
HTTP之请求消息Request
HTTP之响应消息Response
HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
SYN (同步序列编号)ACK(确认字符)
这是由于服务端在LISTEN状态下,收到创建链接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭链接时,当收到对方的FIN报文时,仅仅表示对方再也不发送数据了可是还能接收数据,己方也未必所有数据都发送给对方了,因此己方能够当即close,也能够发送一些数据给对方后,再发送FIN报文给对方来表示赞成如今关闭链接,所以,己方ACK和FIN通常都会分开发送。
当浏览器再次访问一个已经访问过的资源时,它会这样作:
其实 VNode 是对真实 DOM 的一种抽象描述,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等,其它属性都是都是用来扩展 VNode 的灵活性以及实现一些特殊 feature 的。因为 VNode 只是用来映射到真实 DOM 的渲染,不须要包含操做 DOM 的方法,所以它是很是轻量和简单的。 Virtual DOM 除了它的数据结构的定义,映射到真实的 DOM 实际上要经历 VNode 的 create、diff、patch 等过程。那么在 Vue.js 中,VNode 的 create 是经过以前提到的 createElement 方法建立的,咱们接下来分析这部分的实现。
Object.defineProperty(obj, prop, descriptor)
obj 是要在其上定义属性的对象;prop 是要定义或修改的属性的名称;descriptor 是将被定义或修改的属性描述符。 比较核心的是 descriptor,它有不少可选键值,具体的能够去参阅它的文档。这里咱们最关心的是 get 和 set,get 是一个给属性提供的 getter 方法,当咱们访问了该属性的时候会触发 getter 方法;set 是一个给属性提供的 setter 方法,当咱们对该属性作修改的时候会触发 setter 方法。一旦对象拥有了 getter 和 setter,咱们能够简单地把这个对象称为响应式对象
对象递归调用
数组变异方法的解决方法:代理原型/实例方法
observe
observe 方法的做用就是给非 VNode 的对象类型数据添加一个 Observer,若是已经添加过则直接返回,不然在知足必定条件下去实例化一个 Observer 对象实例。
observe 的功能就是用来监测数据的变化.
Observer 是一个类,它的做用是给对象的属性添加 getter 和 setter,用于依赖收集和派发更新:
依赖收集和派发更新
收集依赖的目的是为了当这些响应式数据发生变化,触发它们的 setter 的时候,能知道应该通知哪些订阅者去作相应的逻辑处理,咱们把这个过程叫派发更新,其实 Watcher 和 Dep 就是一个很是经典的观察者设计模式的实现
派发更新就是数据发生变化的时候,触发 setter 逻辑,把在依赖过程当中订阅的的全部观察者,也就是 watcher,都触发它们的 update 过程,这个过程又利用了队列作了进一步优化,在 nextTick 后执行全部 watcher 的 run,最后执行它们的回调函数
vue编译Compile的过程主要分如下几步 parse(生成AST)=> optimize(优化静态节点) => generate(生成render function)
// 解析模板字符串生成 ASTconst ast = parse(template.trim(), options) //优化语法树 optimize(ast, options) //生成代码const code = generate(ast, options) 复制代码
预防:
使用XSS Filter
<script>
中,能够进行JS编码。使用 HttpOnly Cookie 将重要的cookie标记为httponly,这样的话当浏览器向Web服务器发起请求的时就会带上cookie字段,可是在js脚本中却不能访问这个cookie,这样就避免了XSS攻击利用JavaScript的document.cookie获取cookie。
CSRF:跨站请求伪造,也称 XSRF,是一种挟制用户在当前已登陆的Web应用程序上执行非本意的操做的攻击方法。与 XSS 相比,XSS利用的是用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任。
没有套路,只是面试前攒人品,有错误请指出,谢谢。
要求:垂直两栏,左边固定右边自适应。
查看代码 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
要求:垂直三栏布局,左右两栏宽度固定,中间自适应
查看代码 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
和三栏布局要求相同,不过中间列要写在前面保证优先渲染。
查看代码 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
实现一个三角形
常见题目,经过 border
实现 查看代码
使用 css 实现一个宽高自适应的正方形
查看代码 <metacharset="utf-8">
实现一个 1/4 圆、任意弧度的扇形
有多种实现方法,这里选几种简单方法(我看得懂的)实现。 查看代码 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
实现子元素的水平垂直居中
查看代码
要求:清除浮动
能够经过 clear:both
或 BFC 实现 查看代码
使用 CSS 写一个弹出框效果
查看代码 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
要求:一个
div
内部放不少水平div
,并能够横向滚动。
查看代码 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=div, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
CSS 部分完,总结,Flex 无敌。
Function.prototype.bind = function(context, ...bindArgs) {
// func 为调用 bind 的原函数const func = this;
context = context || window;
if (typeof func !== 'function') {
thrownewTypeError('Bind must be called on a function');
}
// bind 返回一个绑定 this 的函数returnfunction(...callArgs) {
let args = bindArgs.concat(callArgs);
if (thisinstanceof func) {
// 意味着是经过 new 调用的 而 new 的优先级高于 bindreturnnew func(...args);
}
return func.call(context, ...args);
}
}
// 经过隐式绑定实现Function.prototype.call = function(context, ...args) {
context = context || window;
context.func = this;
if (typeof context.func !== 'function') {
thrownewTypeError('call must be called on a function');
}
let res = context.func(...args);
delete context.func;
return res;
}
Function.prototype.apply = function(context, args) {
context = context || window;
context.func = this;
if (typeof context.func !== 'function') {
thrownewTypeError('apply must be called on a function');
}
let res = context.func(...args);
delete context.func;
return res;
}
复制代码
复制代码
// 参考 You Dont Know JavaScript 上卷// 基类functionBase() {
}
// 派生类functionDerived() {
Base.call(this);
}
// 将派生类的原型的原型链挂在基类的原型上Object.setPrototypeOf(Derived.prototype, Base.prototype);
复制代码
复制代码
// 手动实现一个 new 关键字的功能的函数 _new(fun, args) --> new fun(args)function_new(fun, ...args) {
if (typeof fun !== 'function') {
returnnewError('参数必须是一个函数');
}
let obj = Object.create(fun.prototype);
let res = fun.call(obj, ...args);
if (res !== null && (typeof res === 'object' || typeof res === 'string')) {
return res;
}
return obj;
}
复制代码
复制代码
// a instanceof bfunction_instanceof(a, b) {
while (a) {
if (a.__proto__ === b.prototype) returntrue;
a = a.__proto__;
}
returnfalse;
}
复制代码
复制代码
// foo 函数将会被调用 传入后台返回的数据functionfoo(data) {
console.log('经过jsonp获取后台数据:', data);
document.getElementById('data').innerHTML = data;
}
/**
* 经过手动建立一个 script 标签发送一个 get 请求
* 并利用浏览器对 <script> 不进行跨域限制的特性绕过跨域问题
*/
(functionjsonp() {
let head = document.getElementsByTagName('head')[0]; // 获取head元素 把js放里面let js = document.createElement('script');
js.src = 'http://domain:port/testJSONP?a=1&b=2&callback=foo'; // 设置请求地址
head.appendChild(js); // 这一步会发送请求
})();
// 后台代码// 由于是经过 script 标签调用的 后台返回的至关于一个 js 文件// 根据前端传入的 callback 的函数名直接调用该函数// 返回的是 'foo(3)'functiontestJSONP(callback, a, b) {
return`${callback} + (${a + b})`;
}
复制代码
复制代码
感受这个有点无聊了…… 查看代码 // Asynchronous Javascript And XMLfunctionajax(options) { // 选项var method = options.method || 'GET', params = options.params, data = options.data, url = options.url + (params ? '?' + Object.keys(params).map(key => key + '=' + params[key]).join('&') : ''), async = options.async === false ? false : true, success = options.success, headers = options.headers;
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
request = new ActiveXObject('Microsoft.XMLHTTP');
}
request.onstatechange = function() {
/**
readyState:
0: 请求未初始化
1: 服务器链接已创建
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪
status: HTTP 状态码
**/if (request.readyState === 4 && request.status === 200) {
success && success(request.responseText);
}
}
request.open(method, url, async);
if (headers) {
Object.keys(headers).forEach(key => request.setRequestHeader(key, headers[key]));
}
method === 'GET' ? request.send() : request.send(request.data);
}
// e.g.
ajax({
method: 'GET',
url: '...',
success: function(res) {
console.log('success', res);
},
async: true,
params: {
p: 'test',
t: 666
},
headers: {
'Content-Type': 'application/json'
}
})
复制代码
复制代码
functionreduce(arr, callback, initial) {
let i = 0;
let acc = initial === undefined ? arr[i++] : initial;
for (; i < arr.length; i++) {
acc = callback(acc, arr[i], i, arr);
}
return acc;
}
复制代码
复制代码
要求是 yield
后面只能是 Promise
或 Thunk
函数,详见 es6.ruanyifeng.com/#docs/gener…
functionrun(gen) {
let g = gen();
functionnext(data) {
let result = g.next(data);
if (result.done) return result.value;
if (result.value instanceofPromise) {
result.value.then(data => next(data));
} else {
result.value(next);
}
}
return next();
}
// ======== e.g. ==========functionfunc(data, cb) {
console.log(data);
cb();
}
function *gen() {
let a = yieldPromise.resolve(1);
console.log(a);
let b = yieldPromise.resolve(2);
console.log(b);
yield func.bind(null, a + b);
}
run(gen);
/**
output:
1
2
3
**/复制代码
复制代码
老生常谈了,感受不必写太复杂
/**
* 节流函数 限制函数在指定时间段只能被调用一次
* 用法 好比防止用户连续执行一个耗时操做 对操做按钮点击函数进行节流处理
*/functionthrottle(func, wait) {
let timer = null;
returnfunction(...args) {
if (!timer) {
func(...args);
timer = setTimeout(() => {
timer = null;
}, wait);
}
}
}
复制代码
复制代码
/**
* 函数调用后不会被当即执行 以后连续 wait 时间段没有调用才会执行
* 用法 如处理用户输入
*/functiondebounce(func, wait) {
let timer = null;
returnfunction(...args) {
if (timer) clearTimeout(timer); // 若是在定时器未执行期间又被调用 该定时器将被清除 并从新等待 wait 秒
timer = setTimeout(() => {
func(...args);
}, wait);
}
}
复制代码
复制代码
简单实现,基本功能都有了。
const PENDING = 1;
const FULFILLED = 2;
const REJECTED = 3;
functionMyPromise(executor) {
let self = this;
this.resolveQueue = [];
this.rejectQueue = [];
this.state = PENDING;
this.val = undefined;
functionresolve(val) {
if (self.state === PENDING) {
setTimeout(() => {
self.state = FULFILLED;
self.val = val;
self.resolveQueue.forEach(cb => cb(val));
});
}
}
functionreject(err) {
if (self.state === PENDING) {
setTimeout(() => {
self.state = REJECTED;
self.val = err;
self.rejectQueue.forEach(cb => cb(err));
});
}
}
try {
// 回调是异步执行 函数是同步执行
executor(resolve, reject);
} catch(err) {
reject(err);
}
}
MyPromise.prototype.then = function(onResolve, onReject) {
let self = this;
// 不传值的话默认是一个返回原值的函数
onResolve = typeof onResolve === 'function' ? onResolve : (v => v);
onReject = typeof onReject === 'function' ? onReject : (e => { throw e });
if (self.state === FULFILLED) {
returnnew MyPromise(function(resolve, reject) {
setTimeout(() => {
try {
let x = onResolve(self.val);
if (x instanceof MyPromise) {
x.then(resolve);
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
});
});
}
if (self.state === REJECTED) {
returnnew MyPromise(function(resolve, reject) {
setTimeout(() => {
try {
let x = onReject(self.val);
if (x instanceof MyPromise) {
x.then(resolve);
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
});
});
}
if (self.state === PENDING) {
returnnew MyPromise(function(resolve, reject) {
self.resolveQueue.push((val) => {
try {
let x = onResolve(val);
if (x instanceof MyPromise) {
x.then(resolve);
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
});
self.rejectQueue.push((val) => {
try {
let x = onReject(val);
if (x instanceof MyPromise) {
x.then(resolve);
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
});
});
}
}
MyPromise.prototype.catch = function(onReject) {
returnthis.then(null, onReject);
}
MyPromise.all = function(promises) {
returnnew MyPromise(function(resolve, reject) {
let cnt = 0;
let result = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
result[i] = res;
if (++cnt === promises.length) resolve(result);
}, err => {
reject(err);
})
}
});
}
MyPromise.race = function(promises) {
returnnew MyPromise(function(resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
});
}
MyPromise.resolve = function(val) {
returnnew MyPromise(function(resolve, reject) {
resolve(val);
});
}
MyPromise.reject = function(err) {
returnnew MyPromise(function(resolve, reject) {
reject(err);
})
}
复制代码
复制代码
实现原理就是监听 url 的哈希值变化了
<!DOCTYPE html><html><head><title>hash 路由</title></head><body><header><ahref="#home">首页</a><ahref="#center">我的中心页</a><ahref="#help">帮助页</a></header><sectionid="content"></section><script>window.addEventListener('hashchange', (e) => {
let content = document.getElementById('content');
content.innerText = location.hash;
})
</script></body></html>复制代码
复制代码
<!DOCTYPE html><html><head><title>history 路由</title></head><body><header><aonclick="changeRoute(this)"data-path="home">首页</a><aonclick="changeRoute(this)"data-path="center">我的中心页</a><aonclick="changeRoute(this)"data-path="help">帮助页</a></header><sectionid="content"></section><script>functionchangeRoute(route) {
let path = route.dataset.path;
/**
* window.history.pushState(state, title, url)
* state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。
* 也就是说,浏览器会将这个对象序列化之后保留在本地,从新载入这个页面的时候,能够拿到这个对象。
* 若是不须要这个对象,此处能够填 null。
* title:新页面的标题。可是,如今全部浏览器都忽视这个参数,因此这里能够填空字符串。
* url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
*/
changePage(path);
history.pushState({ content: path }, null, path);
}
/**
* 调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。
* 点击后退、前进按钮、或者在 js 中调用 history.back()、history.forward()、history.go() 方法会触发
*/window.addEventListener('popstate', (e) => {
let content = e.state && e.state.content;
changePage(content);
});
functionchangePage(pageContent) {
let content = document.getElementById('content');
content.innerText = pageContent;
}
</script></body></html>复制代码
复制代码
还有一些稍复杂的能够写,有时间再补。