-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前端基础 #21
Comments
js中的继承我们先说下《JS高级程序设计》中提到的组合式继承;继承即为继承父类属性和继承父类方法,组合式继承核心是使用call继承属性;原型对象上挂载要继承的方法;故为代码如下:
缺点:父类构造函数执行了两次, 寄生组合式继承核心思想:复制一个父类的原型对象赋值给子类的prototype,不必为了指定子类的原型而调用超类构造函数。
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
alert(this.name);
};
function SubType(name, age) {
//构造函数式继承--子类构造函数中执行父类构造函数
SuperType.call(this, name);
this.age = age;
}
// 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
alert(this.age);
}
var instance = new SubType("lichonglou");
console.log(instance.name)
// console.log(instance.constructor)//指向SubType 如果没有修正原型的构造函数,则会指向父类构造函数 |
ECMA6设计模式单例模式
发布订阅class EventEmitter {
constructor(){
this._events = {};
}
on(event,callback){//事件绑定
const callbacks = this._events[event] || [];
callbacks.push(callback);
this._events[event] = callbacks;
}
emit(...args){//事件触发
const event = [].shift.call(args);//Array.prototype.shift.call(args); //args.shift();
(this._events[event] || []).forEach(cb=>cb.apply(this,args))
}
off(event,callback){//事件解除
const callbacks = (this._events[event] || []).filter(cb=> cb!==callback);
this._events[event] = callbacks;
}
once(event,callback){//只触发一次,触发后解绑
const wrap = (...args)=>{
callback.apply(this,args);
this.off(event,wrap);
}
this.on(event,wrap);
}
} //on&emit
eventEmitter.on('abc', test);
eventEmitter.emit('abc', 3, 666);//3 666
//off
eventEmitter.off('abc',test);
//once
eventEmitter.once('abc',test);
eventEmitter.emit('abc',666,1);//666 1
eventEmitter.emit('abc',888,1);//不输出 |
依赖注入(Dependency Injection)控制反转(Inversion of Control)提升开发效率 |
跨域optionsW3C规范:跨域请求中,分为简单请求和复杂请求;所谓的简单请求,需要满足如下几个条件:
简单请求不会触发 CORS 预检请求(嗅探请求) |
PromisePromise规范
静态方法Promise.resolve()
then总的来说:then方法提供一个供自定义的回调函数,若传入非函数,则会忽略当前then方法,回调函数中会把上一个then中返回的值当做参数值供当前then方法调用。 实现//Promise 支持异步
class SimplePromise {
constructor(callback) {
this._value = null;//存储value值
this.status = 'pending';
this._onSuccess = [];
this._onFail = [];
let resolve = (val)=>{//传递给外部去主动调用的成功函数
if (this.status === 'pending') {
this.status = 'resolve';
this._value = val;
while (this._onSuccess.length > 0) {//如果队列有方法,则进行调用
let temp = this._onSuccess.shift();
temp(val);
}
}
}
let reject = (val)=>{//传递给外部去主动调用的失败函数
if (this.status === 'pending') {
this.status = 'resolve';
this._value = val;
while (this._onFail.length > 0) {//如果队列有方法,则进行调用
let temp = this._onFail.shift();
temp(val);
}
}
}
callback(resolve, reject)
}
then(onFulfilled, onRejected) {
switch (this.status) {//状态判断
case 'resolve'://成功,调用成功回调函数,并传值
onFulfilled(this._value);
break;
case 'reject'://失败,调用失败回调函数,并传值
onRejected(this._value);
break;
case 'pending'://等待状态,则先将函数放入队列中,等待外部调用触发resolve/reject,再进行调用
this._onSuccess.push(onFulfilled);
this._onFail.push(onRejected);
break;
}
}
}
//测试case
const pro = new SimplePromise(function(res, rej) {
setTimeout(function() {
let random = Math.random() * 10
if (random > 5) {
res("success")
} else {
rej("fail")
}
}, 2000)
}
)
pro.then(function(data) {
console.log(data)
}, function(err) {
console.log(err)
}) Promise.prototype.all = (promises = [])=>{
return new Promise((resolve,reject)=>{
let resultArr = [];
let count = 0;
promises.forEach((p,i)=>{
Promise.resolve(p).then((value)=>{
count++;
resultArr[i] = value;
if(count === promises.length){
resolve(resultArr)
}
},reject)
})
})
}
Promise.prototype.race = (promises = []) => {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
throw new TypeError(`argument must be a array`);
}
for (const p of promises) {
// 有一个成功就返回成功状态的promise
// 有一个失败就返回失败状态的promise
p.then(resolve, reject);
}
});
}
let p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1)
}, 1000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2)
}, 2000)
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(3)
}, 3000)
})
Promise.all([p3, p1, p2]).then(res => {
console.log(res) // [3, 1, 2]
});
Promise.race([p3, p1, p2]).then(res => {
console.log(res) // 1
}); 实现一个异步队列,可以设置并发请求数量,应用场景例如:分片上传可以控制三个任务 class PromiseQueue{
constructor(limit=1){
this._list = [];
this._current= 0;
this._limit = limit;
}
add(promiseFn){
this._list.push(promiseFn);
this.loadNext();
}
loadNext(){
if(this._list.length === 0 || this._limit === this._current) return;
this._current++;
const fn = this._list.shift();
const promise = fn();
promise.then(this.onLoaded.bind(this)).catch(this.onLoaded.bind(this));
}
onLoaded(){
this._current--;
this.loadNext();
}
}
const q = new PromiseQueue(2);
[1, 2, 3, 4, 5].forEach(v => {
q.add(() => new Promise((resolve) => {
setTimeout(() => {
console.log(v);
resolve();
}, 1000);
}));
}); |
防抖&截流function debounce(func, delay){
let id;
return function (...args){
clearTimeout(id);
id = setTimeOut(()=>{
func.apply(this,args)
},delay)
}
} 防抖(debounce) :在事件触发后的n秒之后,再去执行真正需要执行的函数,如果在这n秒之内事件又被触发,则重新开始计时。常见的操作就是搜索,中间不断的输入,我们都可以忽略,只获取最后停止输入的时候,才去请求后端。 //截流
/**
* 连续的方法 延迟,如持续输入数据,搜索
* @param {Function} fn 需要延迟运行的的回调函数
**/
let previous = 0
export const throttle = (fn,delay) => {
return function() {
let now = +new Date();
if (now - previous > delay) {
fn.apply(this, arguments);
previous = now;
}
}
}
//调用地方
/**
* 处理滚动视图的滚动事件
*/
scrollFn(e) {
throttle(this.aaa, 1000)(666);
},
aaa(num){
console.log(this.test,num);
}, 节流(throttling):规定好一个单位时间,触发函数一次。如果在这个单位时间内触发多次函数的话,只有一次是可被执行的。想执行多次的话,只能等到下一个周期里。常见的操作比如滚动事件,每隔n毫秒,我们去请求,或者拖拽,每隔n毫秒改变dom的位置。还比如resize窗口。 |
异常捕获
资源加载的错误捕获imgObj.onerror() Vue、React中Vue有 errorHandler,React有 componentDidCatch 进行错误捕获 |
网络安全XSS跨站脚本攻击方式有很多种,大部分都是进行脚本注入,举个例子:在url里面做操作,将普通访问的url修改为url+script脚本,scrript脚本里面可以做很多功能比如:获取cookie并发送到黑客的服务器(用cookie的httponly解),去提交一个数据...等等。 CSRF跨站请求伪造这个一般就是首先在A这个安全网站登陆,用户打开了B这个危险网站,B网站里面加了一个img标签,src是A网站的一个get请求,这时候浏览器会将A网站的请求发出去,并且自动携带了A的cookie,A网站后台没有任何保护的情况下就以为是用户在操作,从而被攻击。 点击劫持最常见的是恶意网站使用 <iframe> 标签把我方的一些含有重要信息类如交易的网页嵌入进去,然后把 iframe 设置透明,用定位的手段的把一些引诱用户在恶意网页上点击。这样用户不知不觉中就进行了某些不安全的操作。 |
forfor in会输出自身以及原型链上可枚举的属性。 for of原生具备 Iterator 接口的数据结构如下。 Array 类型判断typeof运算数为数字 typeof(x) = "number" instanceofa instanceof A 原理就是a沿着__proto__去查找原型对象,A则沿着prototype去查找,如果两者可以找到相同的则返回true Object.prototype.toString.call()一劳永逸 |
call实现//fun.call(thisArg, arg1, arg2, ...)
Function.prototype.call = Function.prototype.call || function(context){
var context = context || window;
context.fn = this; // 获取调用call的函数
var args = [];
for(var i=1, len=arguments.length ; i<len ; i++){
args.push(arguments[i]);
}
var result = eval('context.fn(' + args +')');
delete context.fn;
return result;
} apply//fun.apply(thisArg,[args]);
Function.prototype.apply = Function.prototype.apply || function(context){
var context = context || window;
context.fn = this;
// var args = [];
// for(var i=1, len=arguments.length ; i<len ; i++){
// args.push(arguments[i]);
// }
var args = arguments[1];
var result = eval('context.fn('+args+')');
delete context.fn;
return result;
} bind//fun.bind(thisArg[, arg1[, arg2[, ...]]])
//基础版本
Function.prototype.bind = Function.prototype.bind || function(context){
var self = this;
var context = context || window;
var bindArgs = Array.prototype.slice.call(arguments,1);
return function(){
var tmpArgs = Array.prototype.slice.call(arguments);
var newArgs = bindArgs.concat(tmpArgs);
return self.apply(context, newArgs);
}
} |
http2.0http1.1的痛点
http2.0
在http2.0不再适用的优化1、域名分片 http2.0适用的优化使用 HTTP/2 尽可能用最少的连接,因为同一个连接上产生的请求和响应越多,动态字典积累得越全,头部压缩效果也就越好,而且多路复用效率高,不会像多连接那样造成资源浪费
参考: |
函数柯里化function add(x){
let sum = x;
function fn (y){
sum+=y
return fn;
}
fn.valueOf=()=>{
return sum;
}
fn.toString=()=>{
return sum;
};
return fn;
}
console.log(add(2)(3));
console.log(add(1)(2)(3)(4)) |
前端模块化外部模块的管理每引用一个外部模块,我们都要去下载对应的js文件引入项目,
内部模块的管理立即调用的函数表达式”(IIFE)去组织模块
从上面的代码比较中我们可以得出AMD规范和CMD规范的区别 一方面,在依赖的处理上 AMD推崇依赖前置,即通过依赖数组的方式提前声明当前模块的依赖 AMD推崇通过返回值的方式对外输出 https://zhuanlan.zhihu.com/p/265632724?utm_source=tuicool&utm_medium=referral |
异步与同步
单线程与多线程
js为什么是单线程?js语言在设计之初就是单线程的,原因其实也比较简单,js主要是针对浏览器使用,浏览器中单线程能够保证页面中DOM的渲染不出现异常,也正是这个原因,所以在现在新标准允许JavaScript脚本多线程中有一个严格的要求,子线程不能够进行DOM操作,来严格把控住,所以所谓js多线程,也就只能做一些业务上面的运算,比如数量非常大的数据清洗等等。
任务队列
Philip Roberts的PPT视频《Help, I'm stuck in an event-loop》中已经非常清晰地说明了关于任务队列的讲解:
宏任务&微任务
宏任务一般包括:整体代码script,setTimeout,setInterval, setImmediate。
微任务:Promise,process.nextTick , Object.observe [已废弃], MutationObserver
https://juejin.im/post/6844903764202094606
浏览器与node的区别
浏览器为:
node为:
浏览器环境下,microtask 的任务队列是每个 macrotask 执行完之后执行。而在 Node.js 中,microtask 会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行 microtask 队列的任务。
SettimeOut
基于上面的任务队列,settimeOut函数也就可以解释一些参数0之类的问题了,方法先执行主线程中执行栈的方法,settimeout中的方法会放置到任务队列,并且先进先出,执行栈执行完之后,去任务队列中拿出方法放入执行栈去执行,直到结束为止。
其中delay的毫秒参数的含义如下,MDN,里面提到一点很重要实际延迟可能比预期的更长;这一点很容易理解:实际延迟的时间是看该延迟函数方法之前的那些主线程执行栈中方法执行的时间以及队列中放入执行栈去执行的时间。如果两者超出了延迟时间,那么就产生了实际延迟可能比预期的更长的现象,下面是实验代码验证这个观点
这个例子验证了一点,1000毫秒的含义,其实表示的是第一次运行到这里是1564650928851左右的时间,等到队列中的方法执行完之后,此时间隔时间如果超过了1000ms,则立即执行,如果低于1000ms,则去等待1000ms之后去执行。
这里就验证了,所谓的延迟100ms并不一定是相对延迟了100ms之后,就要去执行,实际延迟可能比预期的更长所以当队列执行到此时,时间已经延迟大于了100ms,所以就立即去执行log 2这个方法。
The text was updated successfully, but these errors were encountered: