转 前端面试题大全
FrontendPeople/前端面试题大全.md
135 lines (133 sloc) 4.49 KB
1.谈谈Vue和React的区别?
2.new的实现原理
3.从浏览器地址栏输入 URL 到显示网页,这个过程是怎么实现的?
4.JS有哪些方式可以实现继承
5.JS为什么会有原型的概念
6.什么是原型链
7.vuex使用流程
8.说说你常用的css3属性有哪些
9.map和forEach区别
10.typescript和Es6的class区别
11.methods,computed,watch三者区别
12.tcp和udp的区别
13.router有哪些钩子
14.vue有哪几种路由模式
15.Vue的指令有哪些钩子函数
16.如何自定义v-modal
18.你接触过哪些设计模式
19.vue的diff的原来
20.vue如何实现响应式的
21.如何实现去重
22.一个页面加载的流程是怎样的
23.webpack的流程是怎样的
24.css有哪些选择器,优先级顺序是怎样的
25.css如何实现一个三角形
26.说说CDN原理
28.说说async原理
29.项目用过哪些优化方法
30.如果一个请求返回的结果有大量计算逻辑,如何优化
31.webwork如何使用、关闭、怎么知道它的任务处理完成了
32.webwork能够操作dom吗,为什么
33.http和websocket区别
34.websocket如何处理异常,和链接断开的问题
35.Promise.all第一个promise函数出现异常会出现什么样的结果
36.typescript中interface和type的区别
37.const和let区别
39.egg如果实现全局公共函数
40.egg如何注册插件
41.egg中的sequelize有哪些注意事项
42.开发中用过哪些代码优化方法
43.vue diff和react diff区别
44.vue和react有哪些区别
45.为什么要用hooks,它有什么优势和缺点
46.react你是如何处理复杂权限的
47.小程序中如何处理fixed定位手机适配
48.小程序中如何解决只能十个请求并发的问题
49.什么是闭包
50.闭包都有哪些运用场景
51.防抖和节流的区别
52.如果我有1W个mens菜单你会如何优化,并且每个men高度不固定
53.react(16.8)中有哪些api(每个api的运用场景)
54.知道 BFC 吗?使用场景有哪些?
55.如何解决BFC的问题
56.postion有哪些属性值,各个值的区别
57.浏览器的渲染流程
58.什么是回流,什么是重绘
59.哪些操作会引起回流和重绘
60.什么是事件循环
61.为什么react中需要调用super函数,可以不调吗
62.实现获取如下结构的深度
{
name:"A",
children:{
name:"B",
children:{
name:"C",
children:{
name:"D",
children:[]
}
}
}
}
63.实现
function A(){
}
A("dom.body.div")
返回
{
dom:{
body:{
div:{
}
}
}
}
64.http和https区别
65.实现一个H5在线活动页面编辑功能,你会如何实现
66.App内嵌H5是如何和Android通讯的
67.Vue数据流和React数据流有什么区别
68.tcp和udp的区别
69.线程和进程的区别
70.为什么js是单线程的
71.webpack用过哪些插件各自是什么用途
72.如何编写一个webpack插件
73.Vue中的router有哪些钩子函数
74.Vue computed,watch,data区别
75.Vue computed可以使用v-model吗
76.Vue可以在子组件中修改Pros吗,为什么
77.Vue有哪些数据传递方式
78.如何实现一个map函数
79.继承的原理
80.vue2和vue3.0区别
81.为什么需要减少http请求(如果有一个请求耗时800毫秒 有三个请求耗时100,300,800总的耗时不也是一样的吗,那为什么需要减少http请求)
82.什么是事件委托,运用场景是什么
83.什么是事件代理,运用场景是什么
84.如何判断一个数组是否符合波峰波谷规则
85.谈谈你理解的JS垃圾回收机制
86.从输入 url 到页面展示经过了哪些步骤
87.react class 组件和 hooks 的区别
88.webpack热更新原理
89.判断是否为数组有哪几种方法
90.如何解决递归爆栈
91.页面卡顿怎么去定位问题
92.页面加载空白如何定位问题
93.typeof(new Array())返回什么
94.如何定位Node内存泄露
95.什么是盒子模型
96.行内元素有哪些?块级元素有哪些?
97.HTML5新增了哪些Api
98.如何实现左中右三个元素水平垂直居中
99.SSR中遇到过什么问题,需要注意什么
100.ajax和axios、fetch的区别
101.Vue数据流和React数据流的区别
102.如何上传大文件
103.实现居中有哪些方法
104.AMD,CMD和UMD的差异
105.ES6新增了哪些api
FrontendPeople/套卷/字节面试一卷/字节面试一卷01.md
118 lines (83 sloc) 4.61 KB
面试官:说说你对Js 事件循环机制的理解?
我们都知道JavaScript是单线程语言,就是因为单线程的特性,就不得不提js中的同步和异步
一、同步和异步
所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就是同步任务,比如简单的逻辑操作及函数,而异步任务不会立马立马执行,会挪步放到到异步队列中,比如ajax、promise、事件、计时器等等。
也就是先执行同步,主线程结束后再按照异步的顺序再次执行。
二、时间循环(Event Loop)
Event Loop是什么?中文翻译是事件循环,等待主线程中任务全部完成后,再回来把异步队列中任务放到主程序中运行,这样反复的循环,就是事件循环。
先来看组代码
console.log('开始111');
setTimeout(function() {
console.log('setTimeout111');
}, 0);
Promise.resolve().then(function() {
console.log('promise111');
}).then(function() {
console.log('promise222');
});
console.log('开始222');
我们猜想一下上面的代码,会怎样打印?我们知道,肯定是先走同步的代码,从上往下,先打印 “开始111”,再打印“开始222”。
中途的三个异步,进入到了异步队列,等待同步执行完(打印完),返回来再执行异步,所以是后打印出来。
打印的结果先放一放,我们稍后回来再说。现在我们中途插播一段知识点:
三、宏观任务和微观任务(先执行微观任务,再执行宏观任务)
在事件循环中,每进行一次循环操作称为tick,tick 的任务处理模型是比较复杂的,里边有两个词:
分别是 Macro Task (宏任务)和 Micro Task(微任务)。
简单来说:
宏观任务主要包含:
setTimeout、setInterval、script(整体代码)、I/O、UI 交互事件、setImmediate(Node.js 环境)
微观任务主要包括:
Promise、MutaionObserver、process.nextTick(Node.js 环境)
规范:
先执行微观任务,再执行宏观任务
那么我们知道了,Promise 属于微观任务, setTimeout、setInterval 属于宏观任务,先执行微观任务,等微观任务执行完,再执行宏观任务。所以我们再看一下这个代码:
console.log('开始111');
setTimeout(function() {
console.log('setTimeout111');
}, 0);
Promise.resolve().then(function() {
console.log('promise111');
}).then(function() {
console.log('promise222');
});
console.log('开始222');
我们按照步骤来分析下:
1、遇到同步任务,直接先打印 “开始111”。
2、遇到异步 setTimeout ,先放到队列中等待执行。
3、遇到了 Promise ,放到等待队列中。
4、遇到同步任务,直接打印 “开始222”。
5、同步执行完,返回执行队列中的代码,从上往下执行,发现有宏观任务 setTimeout 和微观任务 Promise ,那么先执行微观任务,再执行宏观任务。
所以打印的顺序为:
开始111 、开始222 、 promise111 、 promise222 、 setTimeout111 。
同理,我们再来分析一个代码:
console.log('开始111');
setTimeout(function () {
console.log('timeout111');
});
new Promise(resolve => {
console.log('promise111');
resolve();
setTimeout(() => console.log('timeout222'));
}).then(function () {
console.log('promise222')
})
console.log('开始222');
分析一下:
1、遇到同步代码,先打印 “开始111” 。
2、遇到setTimeout异步,放入队列,等待执行 。
3、中途遇到Promise函数,函数直接执行,打印 “promise111”。
4、遇到setTimeout ,属于异步,放入队列,等待执行。
5、遇到Promise的then等待成功返回,异步,放入队列。
6、遇到同步,打印 “开始222”。
7、执行完,返回,将异步队列中的代码,按顺序执行。有一个微观任务,then后的,所以打印 “promise222”,再执行两个宏观任务 “timeout111” “timeout222”。
所以,打印的顺序为:
开始111 、 promise111 、 开始222 、 promise222 、 timeout111 、 timeout222 .
先执行主任务,把异步任务放入循环队列当中,等待主任务执行完,再执行队列中的异步任务。异步任务先执行微观任务,再执行宏观任务。一直这样循环,反复执行,就是事件循环机制。
参考资料
https://www.cnblogs.com/tangjianqiang/p/13470363.html
字节面试一卷02.md
160 lines (127 sloc) 4.53 KB
面试官:谈谈你对异步回调、promise、async/await的理解?
promise的用法
Promise,简单来说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作的结果)
Promise构造函数接收一个函数作为参数,该函数的两个参数是resolve,reject,它们由JavaScript引擎提供。其中resolve函数的作用是当Promise对象转移到成功,调用resolve并将操作结果作为其参数传递出去;
reject函数的作用是单Promise对象的状态变为失败时,将操作报出的错误作为其参数传递出去。
let p = new Promise((resolve,reject) => {
resolve('success')
});
Promise的then方法
promise的then方法带有以下三个参数:成功回调,失败回调,前进回调,一般情况下只需要实现第一个,后面是可选的。Promise中最为重要的是状态,通过then的状态传递可以实现回调函数链式操作的实现
let p = new Promise((resolve,reject) => {
resolve('success')
})
p.then(result => {
console.log(result);
//success
});
Promise的其他方法
catch用法
function myPromise(res){
return new Promise((resolve,reject)=>{
if(res.data){
resolve("成功数据");
}else{
reject("失败数据");
}
});
}
myPromise().then((message)=>{
console.log(message);
}
)
.catch((message)=>{
console.log(message);
})
这个时候catch执行的是和reject一样的,也就是说如果Promise的状态变为reject时,会被catch捕捉到,不过需要特别注意的是如果前面设置了reject方法的回调函数,·则catch不会捕捉到状态变为reject的情况。catch还有一点不同的是,如果在resolve或者reject发生错误的时候,会被catch捕捉到,这样就能避免程序卡死在回调函数中了。
all用法
romise的all方法提供了并行执行异步操作的能力,在all中所有异步操作结束后才执行回调。
function p1(){
return new Promise((resolve,reject)=>{
resolve("p1完成");
})
}
function p2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("p2完成")
},2000);
})
}
function p3(){
return new Promise((resolve,reject)=>{
resolve("p3完成")
});;
}
Promise.all([p1(),p2(),p3()])
.then((data)=>{
console.log(data);
})
这里可以看到p2的resolve放到一个setTimeout中,最后的.then也会等到所有Promise完成状态的改变后才执行。
race用法
在all中的回调函数中,等到所有的Promise都执行完,再来执行回调函数,race则不同它等到第一个Promise改变状态就开始执行回调函数。将上面的all改为race,得到
Promise.race([p1(),p2(),p3()])
.then(function(data){
console.log(data);
})
async、await
异步编程的最高境界就是不关心它是否是异步。async、await很好的解决了这一点,将异步强行转换为同步处理。 async/await与promise不存在谁代替谁的说法,因为async/await是寄生于Promise,是Generater的语法糖。
用法
async用于申明一个function是异步的,而await可以认为是async wait的简写,等待一个异步方法执行完成。 规则:
1 async和await是配对使用的,await存在于async的内部。否则会报错
2 await表示在这里等待一个promise返回,再接下来执行
3 await后面跟着的应该是一个promise对象,(也可以不是,如果不是接下来也没什么意义了…)
写法:
async function demo() {
let result01 = await sleep(100);
//上一个await执行之后才会执行下一句
let result02 = await sleep(result01 + 100);
let result03 = await sleep(result02 + 100);
// console.log(result03);
return result03;
}
demo().then(result => {
console.log(result);
});`
错误捕获
let p = new Promise((resolve,reject) => {
setTimeout(() => {
reject('error');
},1000);
});
async function demo(params) {
try {
let result = await p;
}catch(e) {
console.log(e);
}
}
demo();
区别:
1 promise是ES6,async/await是ES7
2 async/await相对于promise来讲,写法更加优雅
3 reject状态:
1)promise错误可以通过catch来捕捉,建议尾部捕获错误,
2)async/await既可以用.then又可以用try-catch捕捉
参考资料
cnblogs.com/ming1025/p/13092502.html
blog.csdn.net/qq_37617413/article/details/90637694
developer.mozilla.org
字节面试一卷03.md
27 lines (20 sloc) 1.88 KB
面试官:src和href的区别是什么?
虽然一直在用这两个属性,但是一直没有具体的去区分和了解这两个属性的区别,今天就来看看
href标识超文本引用,用在link和a等元素上,href是引用和页面关联,是在当前元素和引用资源之间建立联系
src表示引用资源,表示替换当前元素,用在img,script,iframe上,src是页面内容不可缺少的一部分。
src是source的缩写,是指向外部资源的位置,指向的内部会迁入到文档中当前标签所在的位置;在请求src资源时会将其指向的资源下载并应用到当前文档中,例如js脚本,img图片和frame等元素。
<script src="js.js"></script>当浏览器解析到这一句的时候会暂停其他资源的下载和处理,直至将该资源加载,编译,执行完毕,图片和框架等元素也是如此,类似于该元素所指向的资源嵌套如当前标签内,这也是为什么要把js饭再底部而不是头部。
<link href="common.css" rel="stylesheet"/>当浏览器解析到这一句的时候会识别该文档为css文件,会下载并且不会停止对当前文档的处理,这也是为什么建议使用link方式来加载css而不是使用@import。
补充:link和@import的区别
两者都是外部引用CSS的方式,但是存在一定的区别:
区别1:
link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。
区别2:
link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
区别3:
link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
区别4:
ink支持使用Javascript控制DOM去改变样式;而@import不支持。
参考资料
https://blog.csdn.net/binlety/article/details/81448195
面试官:如何用Flex实现三栏等宽布局?
思路:外层容器也就是ul设置display:flex,对项目也就是li设置flex:auto,代表 flex: 1 1 auto
* {
list-style: none;
border: 0;
padding: 0;
margin: 0
}
ul {
width: 500px;
height: 200px;
background: red;
display: flex;
margin: auto;
margin-top: 100px;
padding: 0 10px;
align-items: center;
}
li {
background: green;
height: 100px;
width: 500px;
display: inline-block;
margin: 2px;
line-height: 100px;
text-align: center;
flex: auto
}
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</body>
效果
解析:
我们注意到width的设置,外层ul是500,li也是500,但是实际看到的确实li平分了ul的宽度,这是因为设置了flex:auto,代表有剩余空间的话项目等分剩余空间放大,空间不足项目等比例缩小
面试官:如何实现多维数组扁平化?
题目大意:原数组[[0],[2,3,4],1,[1,[2,3]]],输出[0,2,3,4,1,1,2,3]
//判断当前数组是否有子数组
function hasChildArray(arr) {
return arr.some(element => {
if (Array.isArray(element)) {
has = true;
return true;
}
});
}
let sourceArr = [[0], [2, 3, 4], 1, [1, [2, 3]]];
let result = [];
//递归
(function doFunc(arr) {
if (hasChildArray(arr)) {
for (let i = 0, l = arr.length; i < l; i++) {
if (typeof arr[i] == "number") {
result.push(arr[i]);
} else if (Array.isArray(arr[i])) {
doFunc(arr[i]);
}
}
} else {
result=result.concat(arr);
}
})(sourceArr);
console.log(result);
效果
面试官:ES6的语法特性,如何给一个不懂的人讲symbol,应用场景有哪些?
内容概要
前言
Symbol基础 (老司机可跳过)
使用场景讨论
总结
前言
Symbol是ES6引入的一个新特性 —— 新到什么程度呢?ES5之前是没有任何办法可以模拟Symbol的
但是,我们日常开发工作中,直接使用到Symbol的场景似乎很少。我在网上搜了很多资料,对Symbol开始逐渐加深了理解,接下来就谈谈我的一些看法。
Symbol 基础知识
symbol 是一种全新的基本数据类型 (primitive data type)
Symbol()函数会返回symbol类型的值,作为构造函数来说它并不完整,因为它不支持语法:
new Symbol() // Uncaught TypeError: Symbol is not a constructor
每个从Symbol()返回的symbol值都是唯一的,使用Symbol()创建新的symbol值,并用一个可选的字符串作为其描述 —— 描述相同的两个Symbol值依然是不同的
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo'); //描述
console.log(typeof symbol1); // "symbol"
console.log(symbol2 === 42); // false
console.log(symbol3.toString()); // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo')); // false
一个symbol值能作为对象属性的标识符 —— 这是该数据类型仅有的目的(划重点)
const obj = {};
const myPrivateMethod = Symbol();
obj[myPrivateMethod] = function() {...};
当一个 symbol 类型的值在属性赋值语句中被用作标识符,该属性(像这个 symbol 一样)是匿名的, 并且是不可枚举的 —— 因为这个属性是不可枚举的,它不会在循环结构 for( ... in ...) 中作为成员出现,也因为这个属性是匿名的,它同样不会出现在 Object.getOwnPropertyNames() 的返回数组里。 这个属性可以通过创建时的原始 symbol 值访问到,或者通过遍历 Object.getOwnPropertySymbols() 返回的数组。 在上面的代码示例中,只有通过保存在变量 myPrivateMethod的值可以访问到对象属性(划重点)
const obj = {};
const aProperty = Symbol("a");
obj[aProperty] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";
for (let i in obj) {
console.log(i); // 输出 "c" 和 "d"
}
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(a), Symbol(b) ]
console.log(obj[aProperty]); // a
console.log(obj[Symbol.for("b")]); // b
全局共享的Symbol
JavaScript 有个全局 symbol 注册表
Symbol.for() 参数为字符串
使用给定的key搜索现有的symbol,如果找到则返回该symbol。否则将使用给定的key在全局symbol注册表中创建一个新的symbol
Symbol.keyFor() 参数为Symbol
从全局symbol注册表中,为给定的symbol检索一个共享的key(字符串)
const a = Symbol.for('foo');
const b = Symbol.for('foo');
console.log(a === b); //true
const aKey = Symbol.keyFor(a);
console.log(aKey); //foo
const isEqual = Symbol.keyFor(Symbol.for("tokenString")) === "tokenString";
console.log(isEqual); //true
Symbol 类具有一些静态属性,这类属性只有几个, 即所谓的众所周知的 symbol。
它们是在某些内置对象中找到的某些特定方法属性的 symbol。 暴露出这些 symbol 使得可以直接访问这些行为;这样的访问可能是有用的,例如在定义自定义类的时候。 普遍的 symbol 的例子有:“Symbol.hasInstance”用于 instanceof ,“Symbol.iterator”用于类似数组的对象,“Symbol.search”用于字符串对象
以Symbol.hasInstance为例:
class MyClass {
static [Symbol.hasInstance](lho) {
return Array.isArray(lho);
}
}
console.log([1,2,3] instanceof MyClass); // true
所有众所周知的 symbol 列表(本文不详细介绍,有兴趣的自行查阅文档):
Symbol.iterator
Symbol.asyncIterator
Symbol.match
Symbol.replace
Symbol.search
Symbol.split
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.unscopables
Symbol.species
Symbol.toPrimitive
Symbol.toStringTag
(对这些众所周知的Symbol有点困惑?不要紧,本文后面会进一步阐述这些的使用场景)
Symbol 的使用场景
从上面的 Symbol 基础知识,我们知道有且仅有 2 种途径可以创建 Symbol 值 —— Symbol() 函数 和 Symbol.for() 方法
使用 Symbol() 函数创建出来的 Symbol 是独一无二的(参数字符串对此毫不影响)
for (let i = 0; i < 100; i++) {
Symbol('test')
}
即 上面代码创建的100个Symbol值都互不相同
这种唯一性在某些定义常量的场景带来了极大的便利
使用场景一:定义常量
假设你正在开发一个日志记录模块,你希望提供 DEBUG,INFO,WARN 三种级别的日志
log.levels = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');
在上面代码中,其实我们并不关心 DEBUG,INFO,WARN 的值的意义,我们仅仅是想获得三个唯一的值作为标志而已
假如我们使用数字来代替上面的Symbol
log.levels = {
DEBUG: 1,
INFO: 2,
WARN: 3,
};
在新增一个 ERROR级别的时候,我们还要小心不能跟之前的值(1,2,3中的任意一个)相同;或者当我们手滑不小心,把 INFO 的值写成 1 时,DEBUG 和 INFO 的日志就混淆在一起了 —— 这些麻烦的根源在于 number 的值并不是独一无二的
(p.s. Symbol 在这边的作用有点类似于Java中 的 Enum )
使用场景二:在对象中存放自定义元数据
对象中的元数据可以理解为 —— 相对次要的,不影响对象内容的特殊属性。
对象的主要内容和属性是那些可以由 Object.getOwnProperties() 获取到的属性。
自定义元数据可以
给目标对象添加标记,以便做特殊处理 作为对象内部的一个“私有”属性,例:
const size = Symbol('size');
class Collection {
constructor() {
this[size] = 0;
}
add(item) {
this[this[size]] = item;
this[size]++;
}
static sizeOf(instance) {
return instance[size];
}
}
const x = new Collection();
console.log(Collection.sizeOf(x) === 0); // true
x.add('foo');
console.log(Collection.sizeOf(x) === 1); // true
console.log(Object.keys(x)); //['0']
console.log(Object.getOwnPropertyNames(x)); // ['0']
console.log(Object.getOwnPropertySymbols(x)); // [ Symbol(size) ]
( 当然,上面这个例子完全可以使用 function 和 闭包实现真正的私有变量,并且我个人也推荐这么做;这边的代码仅做Symbol的一个使用例子)
提醒:由于Symbol对应的属性是不可遍历的,因此 JSON.stringfy 也不会碰这些元数据。
使用场景三:在工具库开发中埋下hook(钩子函数)接入点
仍然以日志库为例,我们希望用户调用我们的 log 方法时可以自定义某些特殊对象的打印格式。
import console from 'my-console-lib';
const inspect = console.Symbols.INSPECT; // 由我们的库导出的一个Symbol值
const myVeryOwnObject = {};
console.log(myVeryOwnObject); // 输出 `{}`
myVeryOwnObject[inspect] = function () { return 'DUUUDE'; }; // 用户可以给目标对象设置hook函数
console.log(myVeryOwnObject); // 输出 `DUUUDE` -- 优先执行用户的hook
我们的 log 方法可以这么实现
console.log = function (...items) {
let output = '';
for(const item of items) {
if (typeof item[console.Symbols.INSPECT] === 'function') {
output += item[console.Symbols.INSPECT](item);
} else {
output += console.inspect[typeof item](item);
}
output += ' ';
}
process.stdout.write(output + '\n');
}
举个真实的例子:
在redux的源码中(github链接)
[Symbol.observable](): Observable<S>
使用了 Symbol.observable 这么一个 Symbol 作为 observable/reactive 库的接入口。
而 Symbol.observable 来自于一个规范 https://github.com/tc39/proposal-observable ,有兴趣的读者可以进一步研读。
使用场景四:类似lodash的工具库
上面 Symbol基础章节中提到了“众所周知的Symbol”,它们通常可以用来定制对象的一些行为。
在日常开发中,其实我们很少能接触到这类需求。但是,在类似lodash这种工具库中,为了能做到广泛的适用性,就需要检查目标对象是否定义了这些特殊的Symbol,同时也要依据规范在返回的对象中设置好这些Symbol对应的属性。
举个例子: lodash 的 toArray.js
/** Built-in value references. */
const symIterator = Symbol.iterator // 众所周知的Symbol
function toArray(value) {
if (!value) {
return []
}
if (isArrayLike(value)) {
return isString(value) ? stringToArray(value) : copyArray(value)
}
if (symIterator && value[symIterator]) { // 如果value包含这个Symbol,就利用这个Symbol遍历value的值
return iteratorToArray(value[symIterator]())
}
const tag = getTag(value)
const func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values)
return func(value)
}
export default toArray
除非你的工作项目就是这类库,否则对这些特殊的Symbol只需要简单了解就足够了(个人看法)。
总结
Symbol 是 ES6 引入的一个全新的基础数据类型,它只拥有一些少量且简单的特性。
在合适的场景下,Symbol能发挥出更高效且灵活的作用。
欢迎分享你对Symbol的理解和使用场景,如果觉得这篇文章对你有帮助,记得一键三连!
参考资料
https://zhuanlan.zhihu.com/p/183874695
面试官:字节面试官:requestAnimationFrame 是宏任务还是微任务?
宏任务和微任务
微任务:
process.nextTick
MutationObserver
Promise.then catch finally
宏任务:
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame
从上面可以看出宏任务和微任务的区别
宏任务:
是没有使用回调,且又不按照代码的执行顺序执行的任务
微任务:
首先他也是不按照代码顺序执行的,但是他有回调,比如外面promise,我们可以反复掉用then
参考资料
https://blog.csdn.net/weixin_45581741/article/details/108737474
https://zhuanlan.zhihu.com/p/360507457
面试官:使用React hooks如何只让下面这段代码的子组件只render一次?
>代码大致如下,子组件有自己的状态但受父组件的控制,当父组件count更新的时候,需要将子组件的number和父组件的count保持同步,但是使用useEffect和useMemo都会让子组件render两次,有什么方法可以只render一次吗?
import React, { useEffect, useMemo, useState } from 'react'
function A() {
const [count, setCount] = useState(0)
return <div>
<p>我是父组件</p>
<p>父组件的count是{count}</p>
<button onClick={() => setCount(count + 1)}>click</button>
<B count={count} />
</div>
}
const B = React.memo(({count}: {count: number}) => {
const [number, setNumber] = useState(0)
useMemo(() => {
setNumber(count)
}, [count])
console.log('子组件render')
return <div>
<p>我是子组件</p>
<p>子组件的number是{number}</p>
<button onClick={() => setNumber(number + 1)}>click</button>
</div>
})
export default A
解析
import React, { useEffect, useMemo, useState, useRef } from 'react';
function A() {
const [count, setCount] = useState(0);
return (
<div>
<p>我是父组件</p>
<p>父组件的count是{count}</p>
<button onClick={() => setCount(count + 1)}>click</button>
<B count={count} />
</div>
);
}
const B = React.memo(({ count }: { count: number }) => {
const numberRef = useRef(0);
const [, update] = useState({});
const updateNumber = () => {
numberRef.current++;
update({});
};
useMemo(() => {
numberRef.current = count;
}, [count]);
console.log('子组件render');
return (
<div>
<p>我是子组件</p>
<p>子组件的number是{numberRef.current}</p>
<button onClick={updateNumber}>click</button>
</div>
);
});
使用useRef保存子组件状态,当父组件更新时,直接更新ref值;当子组件click时,在更新ref值后,再调用一次update触发子组件渲染。
在线示例:
https://codesandbox.io/s/headless-dawn-vvyu9
参考资料
https://www.zhihu.com/people/qiqiboy-81
https://www.zhihu.com/question/444068787
https://www.zhihu.com/question/444068787/answer/1739616037
------------------
FrontendPeople/套卷/每日一题面试题/1.谈谈Vue和React的区别?.md
43 lines (38 sloc) 3 KB
面试题专栏说明
最近刚好换了工作,把最近面试,被面的问题,以及现在大多数出现的面试题以一个专题的形式一次性总结,一天一题,现在总共汇总了150道题,感觉如果这些题都能答出来25k稳如老狗了,每道题目答案没有多余扯皮的部分,就是单纯的答案。
谈谈Vue和React的区别?
1.数据流的不同
组件与DOM之间可以通过 v-model 双向绑定(双向数据流)
React单向数据响应,需要手动setState
2.监听数据变化的实现原理不同
Vue通过 getter/setter
React默认是通过比较引用的方式(diff)进行的
3.代码的复用
Vue是才有公共组件或者mixin的方式实现代码的复用
React是通过 HoC (高阶组件)实现代码的复用
4.组件通信的区别
Vue是通过props、$emit/$on、$parent/$children、EventBus、vuex、$root等方式实现组件通信
React可以通过props向子组件传递数据或者回调或者context实现组件通信
5.模板渲染方式的不同
Vue是通过一种拓展的HTML语法进行渲染(其实Vue也是可以使用JSX) Vue是在和组件JS代码分离的单独的模板中,通过指令来实现的,比如条件语句就需要 v-if 来实现
React 是通过JSX渲染模板
React是在组件JS代码中,通过原生JS实现模板中的常见语法,比如插值,条件,循环等,都是通过JS语法实现的
6.diff算法不同
vue比对节点,当节点元素类型相同,但是className不同,认为是不同类型元素,删除重建,而react会认为是同类型节点,只是修改节点属性
vue的列表比对,采用从两端到中间的比对方式,而react则采用从左到右依次比对的方式。当一个集合,只是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移动到第一个。总体上,vue的对比方式更高效。
7.Vuex 和 Redux 的区别
Vuex 更加灵活一些,组件中既可以 dispatch action 也可以 commit updates,而 Redux 中只能进行 dispatch,并不能直接调用 reducer 进行修改
Redux 使用的是不可变数据,而Vuex的数据是可变的。Redux每次都是用新的state替换旧的state,而Vuex是直接修改
Redux 在检测数据变化的时候,是通过 diff 的方式比较差异的,而Vuex其实和Vue的原理一样,是通过 getter/setter来比较的
8.数据声明和使用的方式不同
vue中的数据由data属性在Vue对象中进行管理,react中的数据由state属性管理;
vue通过slot插槽进行嵌套传递,react通过“props.children”的方式将标签内的部分传递给子组件。
说明
每天一到面试题,25k+稳如老狗,点击↓关注【鬼哥】我 当前进度【#001题】
参考资料
https://cnblogs.com/mengff/p/12828825.html
https://zhuanlan.zhihu.com/p/43494278
https://m.php.cn/article/464234.html
https://segmentfault.com/a/1190000019208626
-------------
10.typescript和Es6的class区别.md
73 lines (63 sloc) 2 KB
TypeScript是ES6的超集。至于需不需要使用,在于你所需要的场景。
相同点
都是采用extends语法进行继承
在constructor中都需要首先使用super()调用父类构造函数,然后才能获取父类的属性
最终都是通过ES5的原型链进行继承
不同点
typescript class中有属性字段有private、protected、readonly等修饰符
typescript constructor构造函数参数必须定义所属类型
typescript class中可以存着静态方法
typescript class方法参数会类型验证
es6中, 只有静态方法,没有静态属性。
public 共有的。 类的内外都可以使用。
protected 受保护的。 类的内部使用,继承的子类中使用。
private 私有的。 类的内部使用。
ES6
class UserBase {
constructor (color) {
this.color = color;
}
static showColor () {
//color ==>undefined
console.log(`我是一个有颜色的公有静态函数,看看我的颜色${this.color}`)
}
}
class User extends UserBase {
constructor (userName, userAge) {
super("yellow");
this.userName=userName;
this.userAge=userAge;
}
showInfo(){
console.log(`名字${this.userName}今年:${this.userAge}`)
}
}
const user=new User("鬼鬼",18);
user.showInfo();//实例函数
User.showColor()//静态函数
typescript
class UserBase{
static color="yellow";//静态属性
static showColor() {//静态方法
console.log(`我是一个有颜色的公有静态函数,看看我的颜色${this.color}`)
}
}
class User extends UserBase{
private userName: string;
private userAge: number;
constructor(userName : string,userAge:number){
super();//必须调用super才能继承showColor函数
this.userName=userName;
this.userAge=userAge;
}
private showInfo ():void{
console.log(`名字${this.userName}今年:${this.userAge}`)
}
}
const user=new User("鬼鬼",18);
user.showInfo();//实例函数
User.showColor();//静态函数
----------------------
2.new的实现原理.md
41 lines (37 sloc) 1.03 KB
new实现原理
1、创建一个空对象 obj
2、将该对象 obj 的原型链 proto 指向构造函数的原型 prototype,
并且在原型链 proto 上设置 构造函数 constructor 为要实例化的 Fn
3、传入参数,并让 构造函数 Fn 改变指向到 obj,并执行
4、最后返回 obj
例子
类对象
function User(userAge, userName) {
this.userAge = userAge
this.userName = userName
}
User.prototype.showInfo = function() {
console.log('this.userAge :', this.userAge)
console.log('this.userName :', this.userName)
}
模拟new运算符功能函数
function myNew() {
let obj = {}
let arg = Array.prototype.slice.call(arguments, 1)
obj.__proto__ = Fn.prototype
obj.__proto__.constructor = Fn
Fn.apply(obj, arg)
return obj
};
测试
const user = myNew(User,18, '鬼鬼')
参考资料
https://boxuegu.com/ask/detail/12985
https://dazhuanlan.com/2020/02/03/5e37df6bd16b8/
https://cnblogs.com/linjunfu/p/10791467.html
--------------------------------
3.从浏览器地址栏输入 URL 到显示的过程是怎么实现的?.md
30 lines (29 sloc) 1.8 KB
DNS解析,将域名地址解析为ip地址
浏览器DNS缓存
系统DNS缓存
路由器DNS缓存
运营商DNS缓存
再找不到就递归搜索该网址
如果找到了,就会 TCP连接:TCP三次握手
第一次握手,由浏览器发起,告诉服务器,我要发送请求了
第二次握手,由服务器发起,告诉浏览器我准备接受了,你发送吧
第三次握手,由浏览器发送,告诉服务器,我马上发送了,准备接受吧
发送请求报文
发送HTTP协议的通信内容,即请求报
接受响应
响应报文
渲染页面
遇见HTML标记,浏览器调用HTML解析器解析成Token,并构建成dom树
遇见style/link标记,浏览器调用css解析器
遇见script标记,调用javascript解析器,处理script代码(绑定时间,修改dom树/css dom树)
根据渲染树计算布局,计算每个节点的几何信息(布局)
将各个节点颜色绘制到屏幕上(渲染) 注意: 这5个步骤不一定按照顺序执行,如果dom树或cssdom树被修改了,可能会执行多次布局和渲染 往往实际页面中,这些步骤都会执行多次。
断开连接:TCP的四次挥手
第一次挥手,浏览器发起,发送给服务器,我东西发送完了(请求报文),你准备关闭吧
第二次挥手,服务器发起,告诉浏览器,我东西接收完了(请求报文),我准备关闭了,你也准备吧
第三次挥手,服务器发起,告诉浏览器,我东西发送完了(响应报文),你准备关闭吧
第四次挥手,浏览器发起,告诉服务器,我东西接收完了, 我准备关闭了,你也准备吧
参考资料
http://r4v.cn/gqk4o
https://zhuanlan.zhihu.com/p/260125085
https://blog.csdn.net/xiaohu_Blog/article/details/109521828
---------------------------
4.JS有哪些方式可以实现继承.md
144 lines (129 sloc) 3.3 KB
继承的含义:
继承是面向对象编程中的一个重要概念,通过继承可以使子类的实例拥有在父类中定义的属性和方法。
1、原型链继承
function UserBase(){
}
function User(){
}
User.prototype = new UserBase();
将父类的实例作为子类的原型。
(1)不能向构造函数传参,无法实现多继承
(2)来自原型对象的引用属性是所有实例共享的
2、构造继承
实际上使用父类的构造函数来增强子类,等于是把父类的构造函数复制给子类。
function UserBase(){
}
function User(userName) {
UserBase.call(this);
this.userName = userName;
}
let user = new User("鬼鬼")
user.userName;
优点:
(1)可以向构造函数传参数
(2)可以实现多继承,多call几个
缺点:
(1)无法实现函数复用
(2)只能继承父类的属性和方法,不能继承父类的原型
3、实例继承
为父类实例添加新属性,作为子类实例返回。
function UserBase(){
}
function User(userName) {
let userBase = new UserBase();
userBase.userName = userName;
return userBase;
}
let user = new User("鬼鬼")
user.userName;
缺点:无法实现多继承
4、拷贝继承
function UserBase(userName){
}
UserBase.prototype.showInfo = function(){
console.log(this.userName)
}
function User(userName) {
let userBase = new UserBase();
for (let attr in userBase) {
User.prototype[attr] = userBase[attr];
}
this.userName = userName;
}
let user = new User("鬼鬼")
user.showInfo();
优点:支持多继承
缺点:占用内存高,因为要用for in循环来拷贝父类属性/方法
不可枚举方法拷贝不了
5、组合继承
通过调用父类构造函数,继承了父类的属性,并保留了传参的优点。
然后再将父类实例作为子类原型,实现了函数复用。
function UserBase(userName){
this.userName = userName
}
UserBase.prototype.showInfo = function(){
console.log(this.userName)
}
function User (userName){
//call方式
UserBase.call(this,userName)
//apply方式
UserBase.apply(this,[userName])
}
User.prototype = new UserBase()
let user = new User("鬼鬼")
user.showInfo();
优点:
(1)继承父类的属性和方法,也继承了父类的原型
(2)可传参,函数可复用
缺点:
调用了两次父类构造函数
6、寄生组合继承
通过寄生的方式,去掉了父类的实例属性,在调用父类构造函数时,
就不会初始化两次实例方法,避免了组合继承的缺点
function UserBase(userName){
this.userName = userName
}
UserBase.prototype.showInfo = function(){
console.log(this.userName)
}
function User (userName){
UserBase.call(this,userName)
}
User.prototype = Object.create(UserBase.prototype)
User.prototype.constructor = User
let user = new User("鬼鬼")
user.showInfo();
7、Class继承
class UserBase{
constructor(userName){
this.userName = userName
}
showInfo(){
console.log(this.userName)
}
}
class User extends UserBase{
constructor(value){
super(value)
}
}
var user = new User("鬼鬼")
user.showInfo();
参考资料
https://blog.csdn.net/guoqing2016/article/details/106418081/
http://www.bubuko.com/infodetail-2556919.html
----------------
5.JS为什么会有原型的概念.md
55 lines (32 sloc) 3.09 KB
今天的每日一题还是非常有意思的,当一个故事来读吧,如果是面试场景的话,也可以直接回答文章最后总结的两点。
因为早期的浏览器只能用来浏览,不具备与访问者互动的能力。比如,如果网页上有一栏"用户名"要求填写,浏览器就无法判断访问者是否真的填写了,只有让服务器端判断。
如果没有填写,服务器端就返回错误,要求用户重新填写,这太浪费时间和服务器资源了。
这个时候需要一门网页脚本语言,这种脚本语言能够完成一些简单的操作,比如判断用户有没有填写表单。刚好这个时候是向对象编程(object-oriented programming)最兴盛的时期,C++是当时最流行的语言,而Java语言也马上推出。
所以Javascript作者也受到了启发,Javascript里面所有的数据类型都是对象(object),这一点与Java非常相似。但是直接使用java的"继承"机制来实现,又觉得过于笨重,但是,Javascript里面都是对象,必须有一种机制,将所有对象联系起来。所以,javascript作者最后还是设计了"继承"。
但是,他不打算引入"类"(class)的概念,因为一旦有了"类",Javascript就是一种完整的面向对象编程语言了,这好像有点太正式了,而且增加了初学者的入门难度。
他考虑到,C++和Java语言都使用new命令,生成实例。
C++的写法是:
ClassName *object = new ClassName(param);
Java的写法是:
Foo foo = new Foo();
这时,他想到C++和Java使用new命令时,都会调用"类"的构造函数(constructor)。他就做了一个简化的设计,在Javascript语言中,new命令后面跟的不是类,而是构造函数。
但是很快发现用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。
每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。
最终加入了prototype属性的引入
考虑到这一点,作者决定为构造函数设置一个prototype对象属性。
所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
实例对象一旦创建,将自动引用prototype对象的属性和方法。
由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象一样。
面试总结回答
JavaScript采用原型编程,所有对象都能共享原型上的方法,节省内存;
同时基于原型这一实现思想,JavaScript通过找对原型链,方便地实现了继承。
这就是原型编程带来的2个最大好处!!!
内容说明
内容有最简化,如果需要看原始总结请查看阮一峰博客↓
参考资料
http://ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html
https://blog.csdn.net/daigualu/article/details/54772799
-------------------------------------------
6.什么是原型链.md
54 lines (40 sloc) 1.42 KB
说明
今天的内容比较简单,但是也希望大家做个知识回顾。
原型链概念
原型链的核心就是依赖对象的_proto_的指向,当自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到Object时,就没有_proto_指向了。
比如我建了一个对象
function User(userName,userAge){
this.userName=userName;
this.userAge=userAge;
this.toString=()=>{
return `${this.userName}永远${this.userAge}`
}
}
var user = new User("鬼鬼",18);
user.toString()
流程说明
当我们「读取」 user.toString 时,JS 引擎会做下面的事情:
首先查找user 对象本身有没有 toString 方法属性。
因为的定义了,所以会输出
"鬼鬼永远18"
如果我把代码改成
function User(userName,userAge){
this.userName=userName;
this.userAge=userAge;
}
var user = new User("鬼鬼",18);
user.toString()
如果没有就继续查看user.proto 对象有没有 toString 方法属性
因为没有,没有就继续向上查找:user.proto.proto 对象有没有 toString 方法属性
因为user.proto.proto 有toString方法,这个方法为Object对象默认的,所以输出:
"[object Object]"
这个查找过程会一直持续到:找到 toString方法属性 或者 proto 为 null才会结束
就这个链式的查找的过程就叫做【原型链】
--------------------------
7.vuex使用流程是什么样的.md
137 lines (118 sloc) 2.86 KB
这道题在Vue面试岗位还是问的挺多的
一、使用Vuex的目的
实现多组件状态管理,多个组件之间需要数据共享
二、Vuex 的五大核心
其中state和mutation是必须的,其他可根据需求来加
1、state 负责状态管理,类似于vue中的data,用于初始化数据
2、mutation 专用于修改state中的数据,通过commit触发
3、action 可以处理异步,通过dispatch触发,不能直接修改state,首先在组件中通过dispatch触发action,然后在action函数内部commit触发mutation,通过mutation修改state状态值
4、getter Vuex中的计算属性,相当于vue中的computed,依赖于state状态值,状态值一旦改变,getter会重新计算,也就是说,当一个数据依赖于另一个数据发生变化时,就要使用getter
5、module 模块化管理
使用流程
1.创建store实例并且导出 store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
//声明state
state: {
userInfo:{ userName:"" }
},
//声明mutations
mutations: {
setUserInfo(state,userInfo){
state.userInfo = userInfo
}
},
//声明actions
actions: {
setUserInfo({ commit },userInfo){
commit('setUserInfo',userInfo)
}
},
//声明getters
getters:{
userName(state){
return "姓名:"+state.userInfo.userName
}
}
})
export default store
2.引入Vuex
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
render: h => h(App),
store
}).$mount('#app')
3.组件内使用
使用方式一
<template>
<div>
<!-- state使用 -->
<p>{{$store.state.userInfo.userName}}</p>
<!-- getters使用 -->
<p>{{$store.getters.userName}}</p>
<!-- action使用 -->
<p @click="setInfo">存储信息</p>
</div>
</template>
export default {
methods: {
setInfo(){
this.$store.commit('setUserInfo',{
userName:"鬼鬼"
})
}
}
}
使用方式二
<template>
<div>
<!-- state使用 -->
<p>{{userInfo.userName}}</p>
<!-- getters使用 -->
<p>{{userName}}</p>
<!-- action使用 -->
<p @click="setInfo">存储信息</p>
</div>
</template>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
methods: {
...mapActions(['setUserInfo']),
// ...mapMutations(["setUserInfo"]),
setInfo(){
this.setUserInfo({
userName:"鬼鬼"
})
},
computed: {
...mapState({
userInfo: state => state.userInfo
}),
...mapGetters(['userName']),
}
}
参考资料
https://blog.csdn.net/u012967771/article/details/114132555
https://cnblogs.com/leslie1943/p/13370639.html?utm_source=tuicool
Footer
-----------------------
8.说说你常用的css3属性有哪些.md
62 lines (61 sloc) 1.74 KB
1.CSS3边框:
border-radius:CSS3圆角边框。
box-shadow:CSS3边框阴影。
border-image:CSS3边框图片。
2.CSS3背景:
background-size: 属性规定背景图片的尺寸。
background-origin :属性规定背景图片的定位区域。
background-clip:指定背景图片从什么位置开始裁剪
3.CSS3文字效果:
text-shadow:在 CSS3 中,text-shadow 可向文本应用阴影。
word-wrap :单词太长的话就可能无法超出某个区域,允许对长单词进行拆分,并换行到下一行
4.CSS3 2D转换:
transform:通过 CSS3 转换,我们能够对元素进行移动、缩放、转动、拉长或拉伸。
translate():元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标)
transform:translate(50px,100px)
rotate():实现元素的旋转效果
transform:rotate(7deg);
scale():实现元素的缩放效果
transform:scale(1.1,1.1);
skew():实现元素的倾斜效果
transform:skew(10deg,10deg);
matrix(): 方法需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素。
transform: matrix(3, 0, 0, 0.5, 0, 0);
clip-path:clip-path里面的polygon功能,我们可以通过它来绘制多边形
-webkit-clip-path: polygon(0 100%, 50% 0, 100% 100%);
5.CSS3 过渡
transition
transition: width 2s;
6.CSS3 动画
animation
animation:myAnimation 5s infinite;
选择器
div:nth-last-child(n)
div:nth-of-type(n)
div:nth-last-of-type(n)
div:last-child
div:first-of-type
div:only-child
div:only-of-type
div:empty
div:checked
div:enabled
div:disabled
div::selection
div:not(s)
Footer
---------------------
9.forEach()和map()的区别.md
48 lines (42 sloc) 1.16 KB
相同点:
都是循环遍历数组中的每一项
每次执行匿名函数参数一样,分别是item(当前每一项)、index(索引值)、arr(原数组)
只能遍历数组
匿名函数中的this都是指向window;
两种方法都不能用 break 中断
const users = ["鬼鬼", "刘亦菲", "周星驰"];
users.forEach((item, index, arr) => {
console.log(item, index, arr);
},this);
const users = ["鬼鬼", "刘亦菲", "周星驰"];
users.map((item,index,arr)=>{
console.log(item,index,arr)
},this)
不同点:
map()速度比forEach()快;
map()会返回一个新数组,不对原数组产生影响;
map()方法输出可以与其他方法(如reduce()、sort()、filter())链接在一起
性能对比
const users=new Array(10000).fill("鬼鬼");
// 1. forEach()
console.time("forEach");
const newArray = [];
users.forEach((item) => {
newArray.push(item)
});
console.timeEnd("forEach");
// 2. map()
console.time("map");
const newArray2 = users.map ((item) => {
return item
});
console.timeEnd("map");
差距还是挺大的
参考资料
https:/cnblogs.com/kaiqinzhang/p/11496151.html
-------------------
10.typescript和Es6的class区别.md
73 lines (63 sloc) 2 KB
TypeScript是ES6的超集。至于需不需要使用,在于你所需要的场景。
相同点
都是采用extends语法进行继承
在constructor中都需要首先使用super()调用父类构造函数,然后才能获取父类的属性
最终都是通过ES5的原型链进行继承
不同点
typescript class中有属性字段有private、protected、readonly等修饰符
typescript constructor构造函数参数必须定义所属类型
typescript class中可以存着静态方法
typescript class方法参数会类型验证
es6中, 只有静态方法,没有静态属性。
public 共有的。 类的内外都可以使用。
protected 受保护的。 类的内部使用,继承的子类中使用。
private 私有的。 类的内部使用。
ES6
class UserBase {
constructor (color) {
this.color = color;
}
static showColor () {
//color ==>undefined
console.log(`我是一个有颜色的公有静态函数,看看我的颜色${this.color}`)
}
}
class User extends UserBase {
constructor (userName, userAge) {
super("yellow");
this.userName=userName;
this.userAge=userAge;
}
showInfo(){
console.log(`名字${this.userName}今年:${this.userAge}`)
}
}
const user=new User("鬼鬼",18);
user.showInfo();//实例函数
User.showColor()//静态函数
typescript
class UserBase{
static color="yellow";//静态属性
static showColor() {//静态方法
console.log(`我是一个有颜色的公有静态函数,看看我的颜色${this.color}`)
}
}
class User extends UserBase{
private userName: string;
private userAge: number;
constructor(userName : string,userAge:number){
super();//必须调用super才能继承showColor函数
this.userName=userName;
this.userAge=userAge;
}
private showInfo ():void{
console.log(`名字${this.userName}今年:${this.userAge}`)
}
}
const user=new User("鬼鬼",18);
user.showInfo();//实例函数
User.showColor();//静态函数
Footer
-------------------
11.methods,computed,watch三者区别.md
66 lines (64 sloc) 2.02 KB
watch
当数据改变时,直接触发对应操作;
可以监听的数据有三部分,即props,data,computed;
所触发的对应操作函数中包含两个参数,第一个参数是新值newValue,第二个参数是旧值oldValue;
不支持缓存,但支持异步,在数据变化时执行异步或开销较大的操作时使用;
一对多,即一个数据改变时,可以通过触发对应操作改变多个其他的数据。
computed
computed 依赖于 data 中的数据,只要在它的相关依赖发生改变时就会触发computed;
计算属性是基于属性的依赖进行缓存的,当data中的数据未变时,优先取缓存中的东西;
支持缓存,但不支持异步;
多对一或一对一,即一个属性是由其他属性计算而来的,这个属性可能是依赖其他一个或多个属性。
methods
是一个方法,执行的时候需要事件进行触发;
可以在模板或者js中以方法的形式调用
执行次数跟调用次数想对应
使用说明
export default {
data: {
userName: '鬼鬼'
},
watch:{
userName(newValue,oldValue){
console.log(`原来的值:${newValue}`);
console.log(`新的值${oldValue}`);
},
//深度监听
userName:{
handler:function(newValue,oldValue){
console.log(`原来的值:${newValue}`);
console.log(`新的值${oldValue}`);
},
immediate:true,
deep:true
},
},
computed: {
gUserName() {
return this.userName
},
//computed传参
gUserName(keyName){
return function(keyName){
return keyName+this.userName
}
}
},
methods: {
getUserName() {
return this.userName
}
}
}
<template>
<!-- methods -->
<div> 我的名字叫:{{ getUserName() }} </div>
<!-- computed -->
<div> 我的名字叫:{{ gUserName }} </div>
<!-- computed 传参 -->
<div> {{ gUserName("我的名字叫:") }} </div>
</template>
---------------
12.tcp和udp的区别.md
63 lines (46 sloc) 3.72 KB
区别
区别一、是否基于连接
TCP是面向连接的协议,而UDP是无连接的协议。
即TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接。
区别二、可靠性 和 有序性 区别
TCP 提供交付保证(Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输),无差错,不丢失,不重复,且按序到达,也保证了消息的有序性。该消息将以从服务器端发出的同样的顺序发送到客户端,尽管这些消息到网络的另一端时可能是无序的。TCP协议将会为你排好序。
UDP不提供任何有序性或序列性的保证。UDP尽最大努力交付,数据包将以任何可能的顺序到达。
TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
区别三、实时性
UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
区别四、协议首部大小
TCP首部开销20字节; UDP的首部开销小,只有8个字节 。
区别五、运行速度
TCP速度比较慢,而UDP速度比较快,因为TCP必须创建连接,以保证消息的可靠交付和有序性,毕竟TCP协议比UDP复杂。
区别六、拥塞机制
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
区别七、流模式(TCP)与数据报模式(UDP);
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;
UDP是面向报文的 。
区别八、资源占用
TCP对系统资源要求较多,UDP对系统资源要求较少。
TCP被认为是重量级的协议,而与之相比,UDP协议则是一个轻量级的协议。因为UDP传输的信息中不承担任何间接创造连接,保证交货或秩序的的信息。这也反映在用于承载元数据的头的大小。
区别九、应用
每一条TCP连接只能是点到点的;
UDP支持一对一,一对多,多对一和多对多的交互通信 。
于UDP不需要建立连接,所以且适合多播的环境,UDP是大量使用在游戏和娱乐场所。
优点
TCP和UDP的优缺点也很明显了。UDP 优点:简单、传输快。
网速的提升给UDP的稳定性提供可靠网络保障,丢包率很低,如果使用应用层重传,能够确保传输的可靠性。
TCP为了实现网络通信的可靠性,使用了复杂的拥塞控制算法,建立了繁琐的握手过程,由于TCP内置的系统协议栈中,极难对其进行改进。采用TCP,一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接收到后再继续发送,延时会越来越大,基于UDP对实时性要求较为严格的情况下,采用自定义重传机制,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成影响。
缺点
不可靠,不稳定;
UDP应用场景:
面向数据报方式
网络数据大多为短消息
拥有大量Client
对数据安全性无特殊要求
网络负担非常重,但对响应速度要求高
TCP:
优点:可靠 稳定
TCP的可靠体现在TCP在传输数据之前,会有三次握手来建立连接,而且在数据传递时,有确认. 窗口. 重传. 拥塞控制机制,在数据传完之后,还会断开来连接用来节约系统资源。
缺点:慢,效率低,占用系统资源高,易被攻击
TCP应用场景:
当对网络质量有要求时,比如HTTP,HTTPS,FTP等传输文件的协议;POP,SMTP等邮件传输的协议。
原文地址:
https://blog.csdn.net/weixin_39789689/article/details/82560805
------------------------
虾皮一卷01.md
181 lines (143 sloc) 5.79 KB
js数据类型的检测方式,基本数据类型有哪些,null的数据类型是什么,(object),为什么是object?
js数据类型的检测方式
1. typeof
typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。 返回的结果用该类型的字符串(全小写字母)形式表示
包括以下 7 种:
number、boolean、symbol、string、object、undefined、function 等
typeof对于基本数据类型的判断是没有问题的,但是遇到了引用数据类型是不起作用的,只能返回一个object。(typeof(null)输出object 是因为在js中,null表示一个空对象指针)
对于基本类型,除 null 以外,均可以返回正确的结果。
对于 null ,返回 object 类型。
对于 function 返回 function 类型。
2、instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。
在这里需要特别注意的是:instanceof 检测的是原型,我们用一段伪代码来模拟其内部执行过程:
instanceof (A,B) = {
var L = A.__proto__;
var R = B.prototype;
if(L === R) {
// A的内部属性 __proto__ 指向 B 的原型对象
return true;
}
return false;
}
3、constructor 先看一下用法:
console.log(("1").constructor === String);
console.log((1).constructor === Number);
console.log((true).constructor === Boolean);
console.log(([]).constructor === Array);
console.log((function() {}).constructor === Function);
console.log(({}).constructor === Object);
输出结果:
true
true
true
true
true
true
撇去null和undefined,似乎说constructor能用于检测js的基本类型和引用类型。但当涉及到原型和继承的时候,便出现了问题,如下:
function fun() {};
fun.prototype = new Array();
let f = new fun();
console.log(f.constructor===fun);
console.log(f.constructor===Array);
撇去null和undefined,constructor能用于检测js的基本类型和引用类型,但当对象的原型更改之后,constructor便失效了。
从上述过程可以看出,当 A 的 proto 指向 B 的 prototype 时,就认为 A 就是 B 的实例
4.Object.prototype.toString.call()
用法:
var test = Object.prototype.toString;
console.log(test.call("str"));
console.log(test.call(1));
console.log(test.call(true));
console.log(test.call(null));
console.log(test.call(undefined));
console.log(test.call([]));
console.log(test.call(function() {}));
console.log(test.call({}));
结果:
[object String]
[object Number]
[object Boolean]
[object Null]
[object Undefined]
[object Array]
[object Function]
[object Object]
这样一看,似乎能满足js的所有数据类型,那我们看下继承之后是否能检测出来
function fun() {};
fun.prototype = new Array();
let f = new fun();
console.log(Object.prototype.toString.call(fun))
console.log(Object.prototype.toString.call(f))
结果:
[object Function]
[object Object]
可以看出,Object.prototype.toString.call()可用于检测js所有的数据类型。
js基本数据类型
js有5种基本数据类型: number、boolean、undefined、null、string
js null是什么类型?
null:是 Null类型,表示一个 空对象指针 或 尚未存在的对象
即该处不应该有值,使用typeof运算得到 object ,是个特殊对象值,转为数值为 0。
也可以理解是表示程序级的、正常的或在意料之中的值的空缺
作为函数的参数,表示该函数的参数不是对象
作为对象原型链的终点
注意:
null 不是一个对象,但 typeof null === object 原因是不同的对象在底层都会表示为二进制,在 JS 中如果二进制的前三位都为 0,就会被判断为object类型,null 的二进制全为 0,自然前三位也是 0,所以 typeof null === ‘objcet’
undefined:
是Undefined 类型,表示一个 无 的原始值 或 缺少值, 即此处应该有一个值,但还没有定义,使用 typeof undefined === ‘undefined’,转为数值为 NaN。
它是在 ECMAScript 第三版引入的预定义全局变量,为了区分空指针对象 和 未初始化的变量。
也可以理解是表示系统级的、出乎意料的或类似错误的值的空缺
变量被声但没有赋值时
调用函数时,应该提供的参数没有提供时
对象没有赋值的属性时,属性值为 undefined
函数没有返回值时,默认返回值为 undefined
(object),为什么是object
"()"也叫分组运算符,它有两种用法:如果表达式放在圆括号中,作用是求值;如果跟在函数后面,作用是调用函数,把表达式放在圆括号之中,将返回表达式的值
console.log((1)); //1
console.log(('a')); //'a'
console.log((1+2)); // 3
把对象放在圆括号之中,则会返回对象的值,即对象本身
var o = {p:1};
console.log((o));// Object {p: 1}
将函数放在圆括号中,会返回函数本身。如果圆括号紧跟在函数的后面,就表示调用函数,即对函数求值
function f(){return 1;}
console.log((f));// function f(){return 1;}
console.log(f()); // 1
[注意]圆括号运算符不能为空,否则会报错
();//SyntaxError: Unexpected token )
由于圆括号的作用是求值,如果将语句放在圆括号之中,就会报错,因为语句没有返回值
console.log(var a = 1);// SyntaxError: Unexpected token var
console.log((var a = 1));// SyntaxError: Unexpected token var
参考资料
https://segmentfault.com/a/1190000019259209
https://blog.csdn.net/weixin_42878211/article/details/108037109
http://blog.itblood.com/4590.html
https://blog.csdn.net/weixin_34417200/article/details/88861212
----------------------------
虾皮一卷02.md
33 lines (31 sloc) 850 Bytes
2.new的过程发生了什么? 代码实例
function User(userName) {
this.userName = userName;
}
const user = new User("鬼哥");
创建空对象;
var obj = {};
设置新对象的 constructor 属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的 prototype 对象;
obj.__proto__ = User.prototype;
使用新对象调用函数,函数中的 this 被指向新实例对象:
User.call(obj); //{}.构造函数();
如果无返回值或者返回一个非对象值,则将新对象返回;如果返回值是一个新对象的话那么直接直接返回该对象。
if (typeof(result) == "object") {
user = result;
} else {
user = obj;
}
参考资料
https://my.oschina.net/qiilee/blog/4915319
https://developer.aliyun.com/ask/258926
-----------------
虾皮一卷02.md
33 lines (31 sloc) 850 Bytes
2.new的过程发生了什么? 代码实例
function User(userName) {
this.userName = userName;
}
const user = new User("鬼哥");
创建空对象;
var obj = {};
设置新对象的 constructor 属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的 prototype 对象;
obj.__proto__ = User.prototype;
使用新对象调用函数,函数中的 this 被指向新实例对象:
User.call(obj); //{}.构造函数();
如果无返回值或者返回一个非对象值,则将新对象返回;如果返回值是一个新对象的话那么直接直接返回该对象。
if (typeof(result) == "object") {
user = result;
} else {
user = obj;
}
参考资料
https://my.oschina.net/qiilee/blog/4915319
https://developer.aliyun.com/ask/258926
---------------
虾皮一卷03.md
116 lines (96 sloc) 3.96 KB
3.深拷贝 浅拷贝 怎么实现一个深拷贝 出现递归循环怎么办,结束条件是什么?
深拷贝和浅拷贝的区别
深拷贝在于引用类型的时候,浅拷贝只复制地址值,实际上还是指向同一堆内存中的数据,深拷贝则是重新创建了一个相同的数据,二者指向的堆内存的地址值是不同的。这个时候修改赋值前的变量数据不会影响赋值后的变量。
深拷贝实现
1.通过递归方式实现深拷贝
function deepClone(obj) {
var target = {};
for(var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (typeof obj[key] === 'object') {
target[key] = deepClone(obj[key]);
} else {
target[key] = obj[key];
}
}
}
return target;
}
2.通过json的方式实现
function (obj) {
let tmp = JSON.stringify(obj);
let result = JSON.parse(tmp);
return result;
}
3.通过Object.create()实现
function deepCopy(obj) {
var copy = Object.create(Object.getPrototypeOf(obj));
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
}
var obj1 = { a: 1, b: {bc: 50, dc: 100, be: {bea: 1}} };
var obj2 = deepCopy(obj1);
console.log(obj2)
obj1.a = 20;
console.log(obj1)
console.log(obj2)
//Object {a: 1, b: Object}
//Object {a: 20, b: Object}
//Object {a: 1, b: Object}
浅拷贝
如果我们要复制对象的所有属性都不是引用类型时,就可以使用浅拷贝,实现方式就是遍历并复制,最后返回新的对象。
function shallowCopy(obj) {
var copy = {};
// 只复制可遍历的属性
for (key in obj) {
// 只复制本身拥有的属性
if (obj.hasOwnProperty(key)) {
copy[key] = obj[key];
}
}
return copy;
}
如上面所说,我们使用浅拷贝会复制所有引用对象的指针,而不是具体的值,所以使用时一定要明确自己的需求,同时,浅拷贝的实现也是最简单的。
JS内部实现了浅拷贝,如Object.assign(),其中第一个参数是我们最终复制的目标对象,后面的所有参数是我们的即将复制的源对象,支持对象或数组,一般调用的方式为
var newObj = Object.assign({}, originObj);
这样我们就得到了一个新的浅拷贝对象。另外[].slice()方法可以视为数组对象的浅拷贝。
深拷贝出现递归循环怎么办?结束条件是什么?
问题描述
js对象clone是RIA编程中常用方法,但是对象属性之间的循环引用会导致clone的递归进入死循环。
js 代码
var a = {pa1:'av1',pa2:'av2'};
var b = {pb1:'bv1',pb2:'bv2'};
a.pa3 = b;
//b.pb3 = a;
var c = cloneobj(a);
如果不包含注释掉的一行,clone是可以正常进行的。
但是如果引入这一行,即出现了js对象属性的循环引用,clone将进入递归的死循环。
现象
浏览器能够很好的处理这种错误,并抛出“too much recursion"错误,并定位到相应的代码行。 (小声说一句:如果写个js死循环就把浏览器搞死了,B/S应用就没法混了)
办法
如何解决了,通常的办法就是限制递归的深度,例如DWR的‘DWRUtil.toDescriptiveString’
但是窃以为这个并非好的解决办法,因为了对应用造成极大的限制。
我的办法就是,在clone的过程中,记住每个已经clone的对象属性, 并且在对对象进行深度clone之前,首先检查是否已经clone过了,如果是,则返回已clone的引用即可。
因此只要放开示例代码的注释行" // return os[m];"就OK了。
参考资料
https://my.oschina.net/u/1440018/blog/543245
https://segmentfault.com/a/1190000011403163
https://segmentfault.com/a/1190000018193387
Footer
------------------
虾皮一卷04.md
80 lines (58 sloc) 4.99 KB
3.cookies、sessionStorage和localStorage解释及区别?
cookie和session
cookie和session都是用来跟踪浏览器用户身份的会话方式。
区别:
1、保持状态:cookie保存在浏览器端,session保存在服务器端
cookie和session使用方式:
(1)cookie机制:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。
Cookie是服务器发给客户端的特殊信息,cookie是以文本的方式保存在客户端,每次请求时都带上它
(2)session机制:当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。如果用户禁用cookie,则要使用URL重写,可以通过response.encodeURL(url) 进行实现;API对encodeURL的结束为,当浏览器支持Cookie时,url不做任何处理;当浏览器不支持Cookie的时候,将会重写URL将SessionID拼接到访问地址后。
存储内容:
cookie只能保存字符串类型,以文本的方式;session通过类似与Hashtable的数据结构来保存,能支持任何类型的对象(session中可含有多个对象)
存储的大小:
cookie:单个cookie保存的数据不能超过4kb;session大小没有限制。
安全性:
cookie:针对cookie所存在的攻击:Cookie欺骗,Cookie截获;session的安全性大于cookie。
原因如下:
(1)sessionID存储在cookie中,若要攻破session首先要攻破cookie;
(2)sessionID是要有人登录,或者启动session_start才会有,所以攻破cookie也不一定能得到sessionID;
(3)第二次启动session_start后,前一次的sessionID就是失效了,session过期后,sessionID也随之失效。
(4)sessionID是加密的
(5)综上所述,攻击者必须在短时间内攻破加密的sessionID,这很难。
应用场景:
cookie:
(1)判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
(2)保存上次登录的时间等信息。
(3)保存上次查看的页面
(4)浏览计数
session:
(1)Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。
(2)网上商城中的购物车
(3)保存用户登录信息
(4)将某些数据放入session中,供同一用户的不同页面使用
(5)防止用户非法登录
缺点:
cookie:
(1)大小受限
(2)用户可以操作(禁用)cookie,使功能受限
(3)安全性较低
(4)有些状态不可能保存在客户端。
(5)每次访问都要传送cookie给服务器,浪费带宽。
(6)cookie数据有路径(path)的概念,可以限制cookie只属于某个路径下。
#### session:
(1)Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。
(2)依赖于cookie(sessionID保存在cookie),如果禁用cookie,则要使用URL重写,不安全
(3)创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度使用session变量将会导致代码不可读而且不好维护。
Web Storage
sessionStorage:
将数据保存在session对象中。所谓session,是指用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。session对象可以用来保存在这段时间内所要求保存的任何数据。
localStorage:
将数据保存在客户端本地的硬件设备(通常指硬盘,也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用。
这两者的区别在于,sessionStorage为临时保存,而localStorage为永久保存。
WebStorage的目的是克服由cookie所带来的一些限制,当数据需要被严格控制在客户端时,不需要持续的将数据发回服务器。
WebStorage两个主要目标:
(1)提供一种在cookie之外存储会话数据的路径。
(2)提供一种存储大量可以跨会话存在的数据的机制。
参考资料
https://blog.csdn.net/qq_41328247/article/details/108523868
-----------------
虾皮一卷05.md
82 lines (70 sloc) 2.92 KB
5.async await的理解
async+await是es7提出来的概念,它也是为了解决回调地狱的问题,它只是一种语法糖.
从本质上讲,await函数仍然是promise,其原理跟Promise相似.
不过比起Promise之后用then方法来执行相关异步操作,async/await则把异步操作变得更像传统函数操作。
async
async用于声明一个异步函数,该函数执行完之后返回一个 Promise 对象,可以使用 then 方法添加回调函数。请看下面代码:
async function helloAsync(){
return "helloAsync";
}
console.log(helloAsync()); // Promise {<resolved>: "helloAsync"}
helloAsync().then(v=>{
console.log(v); // helloAsync
})
通过上面的代码可以得出结论,async 函数内return的值会被封装成一个Promise对象,由于async函数返回Promise对象,所以该函数可以按照Promise对象标准使用then方法进行后续异步操作。
如果要把async函数方法跟Promise对象方法做对比的话,那么下面的Promise对象异步方法代码是完全相等于上面的async函数异步方法。
var helloAsync = function(){
return new Promise(function(resolve){
resolve("helloAsync");
})
}
console.log(helloAsync())
helloAsync().then(v=>{
console.log(v);
})
async函数运行的时候是同步运行的,Promise对象本身内容也是同步运行,这一点两者也是一致的,只有在then方法的时候才会被放入异步队列。
await
await 操作符用于等待一个 Promise 对象,它只能在异步函数 async function 内部使用。
async函数运行的时候是同步运行,但是当async函数内部存在await操作符的时候,则会把await操作符标示的内容同步执行,await操作符标示的内容之后的代码则被放入异步队列等待。
(await标识的代码表示该代码运行需要一定的时间,所以后续的代码得进异步队列等待)
下面放一段await标准用法:
function testAwait (x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function helloAsync() {
var x = await testAwait ("hello world");
console.log(x);
}
helloAsync ();
其实await多多少少对应了Promise对象异步方法里面的then方法,可以将上面代码改写成下面样式,结果也是一致的:
function testAwait (x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function helloAsync() {
var x = testAwait ("hello world");//此处x是一个Promise对象
x.then(function(value){
console.log(value);
});
}
helloAsync ();
// hello world
上述方法把await去掉,使用then取代,能够起到同样的作用。两者都是把特定区域的代码放到异步队列中执行。
参考资料
https://www.octgoodvps.com/archives/656
https://blog.csdn.net/weixin_41615439/article/details/88896675
------------------
虾皮一卷06.md
67 lines (50 sloc) 3.03 KB
原型链最终的指向
原型链最终的指向是Object的prototype, 而Object中的__proto__是null
如果原型指向改变了, 那么就应该在原型改变指向之后添加原型方法
function Person() {
}
Person.prototype.eat = function () {
console.log("吃东西");
};
var per = new Person();
console.dir(per);
console.dir(Person);
//实例对象中有__proto__原型
//构造函数中有prototype原型
//prototype是对象
//所以,prototype这个对象中也有__proto__,那么指向了哪里
//实例对象中的__proto__指向的是构造函数的prototype
//所以,prototype这个对象中__proto__指向的应该是某个构造函数的原型prototype
//Person的prototype中的__proto__的指向
//console.log(Person.prototype.__proto__);
//per实例对象的__proto__------->Person.prototype的__proto__---->Object.prototype的__proto__是null
console.log(per.__proto__ == Person.prototype); //true
console.log(per.__proto__.__proto__ == Person.prototype.__proto__); //true
console.log(Person.prototype.__proto__ == Object.prototype); //true
console.log(Object.prototype.__proto__); //null
为什么是null,而不是Object.prototype?
首先要明确一点,原型链是指对象的原型链,所以原型链上的所有节点都是对象,不能是字符串、数字、布尔值等原始类型。
另外,规范要求原型链必须是有限长度的(从任一节点出发,经过有限步骤后必须到达一个终点。显然也不能有环。)
那么,应该用什么对象作为终点呢?很显然应该用一个特殊的对象。
好吧,Object.prototype确实是个特殊对象,我们先假设用它做终点。那么考虑一下,当你取它的原型时应该怎么办?即
Object.prototype.__proto__;
应该返回什么?
取一个对象的属性时,可能发生三种情况:
如果属性存在,那么返回属性的值。
如果属性不存在,那么返回undefined。
不管属性存在还是不存在,有可能抛异常。
我们已经假设Object.prototype是终点了,所以看起来不能是情况1。
另外,抛出异常也不是好的设计,所以也不是情况3。
那么情况2呢,它不存在原型属性,返回undefined怎么样?也不好,因为返回undefined一种解释是原型不存在,但是也相当于原型就是undefined。
这样,在原型链上就会存在一个非对象的值。
所以,最佳选择就是null。一方面,你没法访问null的属性,所以起到了终止原型链的作用;
另一方面,null在某种意义上也是一种对象,即空对象,因为null一开始就是为表示一个“空”的对象存在的。这样一来,就不会违反“原型链上只能有对象”的约定。
所以,“原型链的终点是null”虽然不是必须不可的,但是却是最合理的。
参考资料
https://www.cnblogs.com/jane-panyiyun/p/12152721.html
https://www.imooc.com/wenda/detail/422208
----------------
虾皮一卷07.md
320 lines (231 sloc) 7.58 KB
谈谈你对作用域的理解?
涉及内容:
全局作用域
函数作用域
块级作用域
词法作用域
动态作用域 动态作用域跟 this 引用机制相关
全局作用域:
作用域,是指变量的生命周期(一个变量在哪些范围内保持一定值)。
全局变量:
生命周期将存在于整个程序之内。
能被程序中任何函数或者方法访问。
在 JavaScript 内默认是可以被修改的。
全局变量,虽然好用,但是是非常可怕的,这是所有程序员公认的事实。
显式声明:
带有关键字 var 的声明;
var testValue = 123;
var testFunc = function () {
console.log('just test')
};
/**---------全局变量会挂载到 window 对象上------------**/
console.log(window.testFunc) // ƒ () { console.log('just test') }
console.log(window.testValue) // 123
其实,我们写的函数如果不经过封装,也会是全局变量,他的生命周期也就是全局作用域;
隐式声明:
不带有声明关键字的变量,JS 会默认帮你声明一个全局变量!!!
function foo(value) {
result = value + 1; // 没有用 var 修饰
return result;
};
foo(123);// 124
console.log(window.result);
// 124 <= 挂在了 window全局对象上
现在,变量 result 被挂载到 window 对象上了!!!
函数作用域:
函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!!!
function bar() {
var testValue = 'inner';
}
console.log(testValue);
// 报错:ReferenceError: testValue is not defined
通过 return 访问函数内部变量:
function bar(value) {
var testValue = 'inner';
return testValue + value;
}
console.log(bar('fun'));
// "innerfun"
函数就像一个工厂,我们输入一些东西,它在内部加工,然后给我们一个加工产物;
通过 闭包 访问函数内部变量:
function bar(value) {
var testValue = 'inner';
var rusult = testValue + value;
function innser() {
return rusult;
};
return innser();
}
console.log(bar('fun'));
// "innerfun"
关于闭包,我不会在这篇文章过多描述,因为,想要描述闭包,本身需要跟本文章一样的长度;
立即执行函数:
这是个很实用的函数,很多库都用它分离全局作用域,形成一个单独的函数作用域;
(function() {
var testValue = 123;
var testFunc = function () {
console.log('just test');
};
})();
console.log(window.testValue);
// undefined
console.log(window.testFunc);
// undefined
它能够自动执行 (function() { //... })() 里面包裹的内容,能够很好地消除全局变量的影响;
块级作用域:
在 ES6 之前,是没有块级作用域的概念的。如果你有 C++ 或者 Java 经验,想必你对块级作用域并不陌生;
for(var i = 0; i < 5; i++) {
// ...
}
console.log(i) // 5
很明显,用 var 关键字声明的变量,在 for 循环之后仍然被保存这个作用域里;
这可以说明: for() { }仍然在,全局作用域里,并没有产生像函数作用域一样的封闭效果;
如果想要实现 块级作用域 那么我们需要用 let 关键字声明!!!
for(let i = 0; i < 5; i++) {
// ...
}
console.log(i)
// 报错:ReferenceError: i is not defined
在 for 循环执行完毕之后 i 变量就被释放了,它已经消失了!!!
同样能形成块级作用域的还有 const 关键字:
if (true) {
const a = 'inner';
}
console.log(a);
// 报错:ReferenceError: a is not defined
let 和 const 关键字,创建块级作用域的条件是必须有一个 { } 包裹:
{
let a = 'inner';
}
if (true) {
let b = 'inner';
}
var i = 0;
// ......
不要小看块级作用域,它能帮你做很多事情,举个栗子:
举一个面试中常见的例子:
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
// 5 5 5 5 5
}, 200);
};
这几乎是作用域的必考题目,你会觉得这种结果很奇怪,但是事实就是这么发生了;
这里的 i 是在全局作用域里面的,只存在 1 个值,等到回调函数执行时,用词法作用域捕获的 i 就只能是 5;
因为这个循环计算的 i 值在回调函数结束之前就已经执行到 5 了;我们应该如何让它恢复正常呢???
解法1:
调用函数,创建函数作用域
for(var i = 0; i < 5; i++) {
abc(i);
};
function abc(i) {
setTimeout(function() {
console.log(i);
// 0 1 2 3 4
}, 200);
}
这里相当于创建了5个函数作用域来保存,我们的 i 值;
解法2:
采用立即执行函数,创建函数作用域;
for(var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 200);
})(i);
};
原理同上,只不过换成了自动执行这个函数罢了,这里保存了 5 次 i 的值;
解法3:
let 创建块级作用域
for(let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 200);
};
词法作用域:
词法作用域是指一个变量的可见性,及其文本表述的模拟值(《JavaScript函数式编程》);
听起来,十分地晦涩,不过将代码拿来分析就非常浅显易懂了;
testValue = 'outer';
function afun() {
var testValue = 'middle';
console.log(testValue); // "middle"
function innerFun() {
var testValue = 'inner';
console.log(testValue); // "inner"
}
return innerFun();
}
afun();
console.log(testValue); // "outer"
当我们要使用声明的变量时:JS引擎总会从最近的一个域,向外层域查找;
再举一个一个实际的例子:
var testValue = 'outer';
function foo() {
console.log(testValue); // "outer"
}
function bar() {
var testValue = 'inner';
foo();
}
bar();
显然,当 JS 引擎查找这个变量时,发现全局的 testValue 离得更近一些,这恰好和 动态作用域 相反;
如上图所示,下面将讲述与 词法作用域相反的动态作用域;
动态作用域:
在编程中,最容易被低估和滥用的概念就是动态作用域(《JavaScript函数式编程》)。
在 JavaScript 中的仅存的应用动态作用域的地方:this 引用,所以这是一个大坑!!!!!
动态作用域,作用域是基于调用栈的,而不是代码中的作用域嵌套;
作用域嵌套,有词法作用域一样的特性,查找变量时,总是寻找最近的作用域;
同样是,词法作用域,例子2,同一份代码,如果 是动态作用域:
var testValue = 'outer';
function foo() {
console.log(testValue); // "inner"
}
function bar() {
var testValue = 'inner';
foo();
}
bar();
当然,JavaScript 除了this之外,其他,都是根据词法作用域查找!!!
为什么要理解动态作用域呢?因为,这能让你更好地学习 this 引用!!!
参考资料
https://juejin.cn/post/6844903584891420679
---------------
虾皮一卷08.md
66 lines (51 sloc) 3.31 KB
面试官:聊聊你对webpack相关的了解
webpack和grunt以及gulp有什么不同?
grunt和gulp是基于任务运行工具。 我们需要把我们要做的事,分配成各种各样的 任务,grunt和gulp它会自动执行指定的任务,像流水线,把资源放上去,通过不同的插件进行加工,它的插件非常丰富,能够帮我们打造各种的工作流。
webpack是模块打包器,webpack是所有的文件都当作模块来处理,也就是说webpack和grunt以及gulp 是完全不同的两类工具,
什么是bundle? 什么是chunk? 什么是module?
chunk: 代码块 一个chunk可能有很多的模块构造 用于代码的合并和分割
module:模块 在webpack世界中,一切都是模块 一个文件就是一个模块 webpack会从entry开始查找出项目所有的依赖的模块
bundle: webpack打包后生成的文件
什么是loader ? 什么是Plugin ? loader和plugin有什么区别?
webapck默认只能打包JS和JOSN模块,要打包其它模块,需要借助loader,loader就可以让模块中的内容转化成webpack或其它laoder可以识别的内容。
loader就是模块转换化,或叫加载器。不同的文件,需要不同的loader来处理。
plugin是插件,可以参与到整个webpack打包的流程中,不同的插件,在合适的时机,可以做不同的事件。
常见的loader有哪些? 你用的loader有哪些?它们有解决什么问题?
css-loader: 加载css模块 转换成模块化的css 让webpack识别它
style-loader: 把css代码注入到js中,通过DOM操作去加载CSS 把css代码放到了header标签中style标签中
babel-loader 把JS高阶转成JS低阶 pollyfill
eslint-loader: 检查JS代码是否符合某个规则
file-loader: 把文件输出到一个文件夹中,在代码中通过URL引用输出的文件
url-loader: 和file-loader类似,比file-loader强大一个,让小图片直接生成base64
less-loader: 把less代码转化css代码
html-loader: 处理html模块中的插件图片等
webpack中都有哪些插件,这些插件有什么作用?
html-webpack-plugin 自动创建一个HTML文件,并把打包好的JS插入到HTML文件中
clean-webpack-plugin 在每一次打包之前,删除整个输出文件夹下所有的内容
mini-css-extrcat-plugin 抽离CSS代码,放到一个单独的文件中
optimize-css-assets-plugin 压缩css
loader和plugin有什么区别?
loader 叫 加载器 或 转换器。
webpack中一切都是模块,但是webpack默认只能处理js和json模块,如果你想处理非JS模块,就需要借助loader。
让loader帮我们处理。Loader的作用是让webpack可以处理非JS模块 loader会把非JS模块中的内容转成新的内容。
plugin 插件 扩展webpack的功能,让webpack更加强大,在webpack构建的生命周期中,可以执行不同的插件,影响输出的结果。
什么是同源策略?
同源策略是为了保证用户信息安全,是浏览器的机制,防止恶意的网站窃取别人的数据,只允许访问来自同一个站点的资源
同源是指:
协议相同,域名相同,端口相同。这三者相同就是同源。如果不同源,浏览器就会作出限制。
参考资料
https://blog.csdn.net/weixin_46628546/article/details/108954255
-------------
虾皮一卷09.md
65 lines (43 sloc) 3.41 KB
面试官:说说http https的区别,https如何保证安全的?
http https的区别?
一、传输信息安全性不同
1、http协议:是超文本传输协议,信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。
2、https协议:是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全。
二、连接方式不同
1、http协议:http的连接很简单,是无状态的。
2、https协议:是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
三、端口不同
1、http协议:使用的端口是80。
2、https协议:使用的端口是443.
四、证书申请方式不同
1、http协议:免费申请。 2、https协议:需要到ca申请证书,一般免费证书很少,需要交费。传输信息安全性不同、连接方式不同、端口不同、证书申请方式不同
一、传输信息安全性不同
1、http协议:是超文本传输协议,信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。
2、https协议:是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全。
二、连接方式不同
1、http协议:http的连接很简单,是无状态的。
2、https协议:是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
三、端口不同
1、http协议:使用的端口是80。
2、https协议:使用的端口是443.
https做了什么保证它是安全的?
使用,SSL/TLS协议来保证https的安全。
SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。
(1)客户端向服务器端发起SSL连接请求;
(2) 服务器把公钥发送给客户端,并且服务器端保存着唯一的私钥
(3)客户端用公钥对双方通信的对称秘钥进行加密,并发送给服务器端
(4)服务器利用自己唯一的私钥对客户端发来的对称秘钥进行解密,
(5)进行数据传输,服务器和客户端双方用公有的相同的对称秘钥对数据进行加密解密,可以保证在数据收发过程中的安全,即是第三方获得数据包,也无法对其进行加密,解密和篡改。
如何保证公钥不被篡改?
将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。
公钥加密计算量太大,如何减少耗用的时间?
每一次对话(session),客户端和服务器端都生成一个”对话密钥”(session key),用它来加密信息。
由于”对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密”对话密钥”本身,这样就减少了加密运算的消耗时间。
(1) 客户端向服务器端索要并验证公钥。
(2) 双方协商生成”对话密钥”。
(3) 双方采用”对话密钥”进行加密通信。上面过程的前两步,又称为”握手阶段”(handshake)。
参考资料
https://zhuanlan.zhihu.com/p/354021419
https://segmentfault.com/a/1190000039683023
http://blog.itblood.com/1116.html
--------------------
虾皮一卷10.md
24 lines (14 sloc) 1.91 KB
面试官:说说http状态码,4开头和5开头具体有什么区别?
访问网站所反应的信号叫做HTTP状态码,是以三个数字为主的,我们看不到只是浏览器可以检测到这样的信号。
也就是用以表示网页服务器HTTP响应状态的3位数的数字代码,http状态码是由RFC2616规范定义的,并得到了RFC 2518、RFC 2817、RFC 2295、RFC 2774、RFC 4918等规范扩展。
http状态码的第一个数字代表了相应的一种状态,通常共有五种状态形式。
5开头的http状态码
代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。
除非这是一个HEAD 请求,否则服务器应当包含一个解释当前错误状态以及这个状况是临时的还是永久的解释信息实体。浏览器应当向用户展示任何在当前响应中被包含的实体。这些状态码适用于任何响应方法。
4开头的http状态码
代表了客户端看起来可能发生了错误,妨碍了服务器的处理。除非响应的是一个 HEAD 请求,否则服务器就应该返回一个解释当前错误状况的实体,以及这是临时的还是永久性的状况。这些状态码适用于任何请求方法。
浏览器应当向用户显示任何包含在此类错误响应中的实体内容。
如果错误发生时客户端正在传送数据,那么使用TCP的服务器实现应当仔细确保在关闭客户端与服务器之间的连接之前,客户端已经收到了包含错误信息的数据包。
如果客户端在收到错误信息后继续向服务器发送数据,服务器的TCP栈将向客户端发送一个重置数据包,以清除该客户端所有还未识别的输入缓冲,以免这些数据被服务器上的应用程序读取并干扰后者。
参考资料
http://www.xiaokaiseo.com/255.html
FrontendPeople/前端面试题大全.md
135 lines (133 sloc) 4.49 KB
1.谈谈Vue和React的区别?
2.new的实现原理
3.从浏览器地址栏输入 URL 到显示网页,这个过程是怎么实现的?
4.JS有哪些方式可以实现继承
5.JS为什么会有原型的概念
6.什么是原型链
7.vuex使用流程
8.说说你常用的css3属性有哪些
9.map和forEach区别
10.typescript和Es6的class区别
11.methods,computed,watch三者区别
12.tcp和udp的区别
13.router有哪些钩子
14.vue有哪几种路由模式
15.Vue的指令有哪些钩子函数
16.如何自定义v-modal
18.你接触过哪些设计模式
19.vue的diff的原来
20.vue如何实现响应式的
21.如何实现去重
22.一个页面加载的流程是怎样的
23.webpack的流程是怎样的
24.css有哪些选择器,优先级顺序是怎样的
25.css如何实现一个三角形
26.说说CDN原理
28.说说async原理
29.项目用过哪些优化方法
30.如果一个请求返回的结果有大量计算逻辑,如何优化
31.webwork如何使用、关闭、怎么知道它的任务处理完成了
32.webwork能够操作dom吗,为什么
33.http和websocket区别
34.websocket如何处理异常,和链接断开的问题
35.Promise.all第一个promise函数出现异常会出现什么样的结果
36.typescript中interface和type的区别
37.const和let区别
39.egg如果实现全局公共函数
40.egg如何注册插件
41.egg中的sequelize有哪些注意事项
42.开发中用过哪些代码优化方法
43.vue diff和react diff区别
44.vue和react有哪些区别
45.为什么要用hooks,它有什么优势和缺点
46.react你是如何处理复杂权限的
47.小程序中如何处理fixed定位手机适配
48.小程序中如何解决只能十个请求并发的问题
49.什么是闭包
50.闭包都有哪些运用场景
51.防抖和节流的区别
52.如果我有1W个mens菜单你会如何优化,并且每个men高度不固定
53.react(16.8)中有哪些api(每个api的运用场景)
54.知道 BFC 吗?使用场景有哪些?
55.如何解决BFC的问题
56.postion有哪些属性值,各个值的区别
57.浏览器的渲染流程
58.什么是回流,什么是重绘
59.哪些操作会引起回流和重绘
60.什么是事件循环
61.为什么react中需要调用super函数,可以不调吗
62.实现获取如下结构的深度
{
name:"A",
children:{
name:"B",
children:{
name:"C",
children:{
name:"D",
children:[]
}
}
}
}
63.实现
function A(){
}
A("dom.body.div")
返回
{
dom:{
body:{
div:{
}
}
}
}
64.http和https区别
65.实现一个H5在线活动页面编辑功能,你会如何实现
66.App内嵌H5是如何和Android通讯的
67.Vue数据流和React数据流有什么区别
68.tcp和udp的区别
69.线程和进程的区别
70.为什么js是单线程的
71.webpack用过哪些插件各自是什么用途
72.如何编写一个webpack插件
73.Vue中的router有哪些钩子函数
74.Vue computed,watch,data区别
75.Vue computed可以使用v-model吗
76.Vue可以在子组件中修改Pros吗,为什么
77.Vue有哪些数据传递方式
78.如何实现一个map函数
79.继承的原理
80.vue2和vue3.0区别
81.为什么需要减少http请求(如果有一个请求耗时800毫秒 有三个请求耗时100,300,800总的耗时不也是一样的吗,那为什么需要减少http请求)
82.什么是事件委托,运用场景是什么
83.什么是事件代理,运用场景是什么
84.如何判断一个数组是否符合波峰波谷规则
85.谈谈你理解的JS垃圾回收机制
86.从输入 url 到页面展示经过了哪些步骤
87.react class 组件和 hooks 的区别
88.webpack热更新原理
89.判断是否为数组有哪几种方法
90.如何解决递归爆栈
91.页面卡顿怎么去定位问题
92.页面加载空白如何定位问题
93.typeof(new Array())返回什么
94.如何定位Node内存泄露
95.什么是盒子模型
96.行内元素有哪些?块级元素有哪些?
97.HTML5新增了哪些Api
98.如何实现左中右三个元素水平垂直居中
99.SSR中遇到过什么问题,需要注意什么
100.ajax和axios、fetch的区别
101.Vue数据流和React数据流的区别
102.如何上传大文件
103.实现居中有哪些方法
104.AMD,CMD和UMD的差异
105.ES6新增了哪些api
FrontendPeople/套卷/字节面试一卷/字节面试一卷01.md
118 lines (83 sloc) 4.61 KB
面试官:说说你对Js 事件循环机制的理解?
我们都知道JavaScript是单线程语言,就是因为单线程的特性,就不得不提js中的同步和异步
一、同步和异步
所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就是同步任务,比如简单的逻辑操作及函数,而异步任务不会立马立马执行,会挪步放到到异步队列中,比如ajax、promise、事件、计时器等等。
也就是先执行同步,主线程结束后再按照异步的顺序再次执行。
二、时间循环(Event Loop)
Event Loop是什么?中文翻译是事件循环,等待主线程中任务全部完成后,再回来把异步队列中任务放到主程序中运行,这样反复的循环,就是事件循环。
先来看组代码
console.log('开始111');
setTimeout(function() {
console.log('setTimeout111');
}, 0);
Promise.resolve().then(function() {
console.log('promise111');
}).then(function() {
console.log('promise222');
});
console.log('开始222');
我们猜想一下上面的代码,会怎样打印?我们知道,肯定是先走同步的代码,从上往下,先打印 “开始111”,再打印“开始222”。
中途的三个异步,进入到了异步队列,等待同步执行完(打印完),返回来再执行异步,所以是后打印出来。
打印的结果先放一放,我们稍后回来再说。现在我们中途插播一段知识点:
三、宏观任务和微观任务(先执行微观任务,再执行宏观任务)
在事件循环中,每进行一次循环操作称为tick,tick 的任务处理模型是比较复杂的,里边有两个词:
分别是 Macro Task (宏任务)和 Micro Task(微任务)。
简单来说:
宏观任务主要包含:
setTimeout、setInterval、script(整体代码)、I/O、UI 交互事件、setImmediate(Node.js 环境)
微观任务主要包括:
Promise、MutaionObserver、process.nextTick(Node.js 环境)
规范:
先执行微观任务,再执行宏观任务
那么我们知道了,Promise 属于微观任务, setTimeout、setInterval 属于宏观任务,先执行微观任务,等微观任务执行完,再执行宏观任务。所以我们再看一下这个代码:
console.log('开始111');
setTimeout(function() {
console.log('setTimeout111');
}, 0);
Promise.resolve().then(function() {
console.log('promise111');
}).then(function() {
console.log('promise222');
});
console.log('开始222');
我们按照步骤来分析下:
1、遇到同步任务,直接先打印 “开始111”。
2、遇到异步 setTimeout ,先放到队列中等待执行。
3、遇到了 Promise ,放到等待队列中。
4、遇到同步任务,直接打印 “开始222”。
5、同步执行完,返回执行队列中的代码,从上往下执行,发现有宏观任务 setTimeout 和微观任务 Promise ,那么先执行微观任务,再执行宏观任务。
所以打印的顺序为:
开始111 、开始222 、 promise111 、 promise222 、 setTimeout111 。
同理,我们再来分析一个代码:
console.log('开始111');
setTimeout(function () {
console.log('timeout111');
});
new Promise(resolve => {
console.log('promise111');
resolve();
setTimeout(() => console.log('timeout222'));
}).then(function () {
console.log('promise222')
})
console.log('开始222');
分析一下:
1、遇到同步代码,先打印 “开始111” 。
2、遇到setTimeout异步,放入队列,等待执行 。
3、中途遇到Promise函数,函数直接执行,打印 “promise111”。
4、遇到setTimeout ,属于异步,放入队列,等待执行。
5、遇到Promise的then等待成功返回,异步,放入队列。
6、遇到同步,打印 “开始222”。
7、执行完,返回,将异步队列中的代码,按顺序执行。有一个微观任务,then后的,所以打印 “promise222”,再执行两个宏观任务 “timeout111” “timeout222”。
所以,打印的顺序为:
开始111 、 promise111 、 开始222 、 promise222 、 timeout111 、 timeout222 .
先执行主任务,把异步任务放入循环队列当中,等待主任务执行完,再执行队列中的异步任务。异步任务先执行微观任务,再执行宏观任务。一直这样循环,反复执行,就是事件循环机制。
参考资料
https://www.cnblogs.com/tangjianqiang/p/13470363.html
字节面试一卷02.md
160 lines (127 sloc) 4.53 KB
面试官:谈谈你对异步回调、promise、async/await的理解?
promise的用法
Promise,简单来说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作的结果)
Promise构造函数接收一个函数作为参数,该函数的两个参数是resolve,reject,它们由JavaScript引擎提供。其中resolve函数的作用是当Promise对象转移到成功,调用resolve并将操作结果作为其参数传递出去;
reject函数的作用是单Promise对象的状态变为失败时,将操作报出的错误作为其参数传递出去。
let p = new Promise((resolve,reject) => {
resolve('success')
});
Promise的then方法
promise的then方法带有以下三个参数:成功回调,失败回调,前进回调,一般情况下只需要实现第一个,后面是可选的。Promise中最为重要的是状态,通过then的状态传递可以实现回调函数链式操作的实现
let p = new Promise((resolve,reject) => {
resolve('success')
})
p.then(result => {
console.log(result);
//success
});
Promise的其他方法
catch用法
function myPromise(res){
return new Promise((resolve,reject)=>{
if(res.data){
resolve("成功数据");
}else{
reject("失败数据");
}
});
}
myPromise().then((message)=>{
console.log(message);
}
)
.catch((message)=>{
console.log(message);
})
这个时候catch执行的是和reject一样的,也就是说如果Promise的状态变为reject时,会被catch捕捉到,不过需要特别注意的是如果前面设置了reject方法的回调函数,·则catch不会捕捉到状态变为reject的情况。catch还有一点不同的是,如果在resolve或者reject发生错误的时候,会被catch捕捉到,这样就能避免程序卡死在回调函数中了。
all用法
romise的all方法提供了并行执行异步操作的能力,在all中所有异步操作结束后才执行回调。
function p1(){
return new Promise((resolve,reject)=>{
resolve("p1完成");
})
}
function p2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("p2完成")
},2000);
})
}
function p3(){
return new Promise((resolve,reject)=>{
resolve("p3完成")
});;
}
Promise.all([p1(),p2(),p3()])
.then((data)=>{
console.log(data);
})
这里可以看到p2的resolve放到一个setTimeout中,最后的.then也会等到所有Promise完成状态的改变后才执行。
race用法
在all中的回调函数中,等到所有的Promise都执行完,再来执行回调函数,race则不同它等到第一个Promise改变状态就开始执行回调函数。将上面的all改为race,得到
Promise.race([p1(),p2(),p3()])
.then(function(data){
console.log(data);
})
async、await
异步编程的最高境界就是不关心它是否是异步。async、await很好的解决了这一点,将异步强行转换为同步处理。 async/await与promise不存在谁代替谁的说法,因为async/await是寄生于Promise,是Generater的语法糖。
用法
async用于申明一个function是异步的,而await可以认为是async wait的简写,等待一个异步方法执行完成。 规则:
1 async和await是配对使用的,await存在于async的内部。否则会报错
2 await表示在这里等待一个promise返回,再接下来执行
3 await后面跟着的应该是一个promise对象,(也可以不是,如果不是接下来也没什么意义了…)
写法:
async function demo() {
let result01 = await sleep(100);
//上一个await执行之后才会执行下一句
let result02 = await sleep(result01 + 100);
let result03 = await sleep(result02 + 100);
// console.log(result03);
return result03;
}
demo().then(result => {
console.log(result);
});`
错误捕获
let p = new Promise((resolve,reject) => {
setTimeout(() => {
reject('error');
},1000);
});
async function demo(params) {
try {
let result = await p;
}catch(e) {
console.log(e);
}
}
demo();
区别:
1 promise是ES6,async/await是ES7
2 async/await相对于promise来讲,写法更加优雅
3 reject状态:
1)promise错误可以通过catch来捕捉,建议尾部捕获错误,
2)async/await既可以用.then又可以用try-catch捕捉
参考资料
cnblogs.com/ming1025/p/13092502.html
blog.csdn.net/qq_37617413/article/details/90637694
developer.mozilla.org
字节面试一卷03.md
27 lines (20 sloc) 1.88 KB
面试官:src和href的区别是什么?
虽然一直在用这两个属性,但是一直没有具体的去区分和了解这两个属性的区别,今天就来看看
href标识超文本引用,用在link和a等元素上,href是引用和页面关联,是在当前元素和引用资源之间建立联系
src表示引用资源,表示替换当前元素,用在img,script,iframe上,src是页面内容不可缺少的一部分。
src是source的缩写,是指向外部资源的位置,指向的内部会迁入到文档中当前标签所在的位置;在请求src资源时会将其指向的资源下载并应用到当前文档中,例如js脚本,img图片和frame等元素。
<script src="js.js"></script>当浏览器解析到这一句的时候会暂停其他资源的下载和处理,直至将该资源加载,编译,执行完毕,图片和框架等元素也是如此,类似于该元素所指向的资源嵌套如当前标签内,这也是为什么要把js饭再底部而不是头部。
<link href="common.css" rel="stylesheet"/>当浏览器解析到这一句的时候会识别该文档为css文件,会下载并且不会停止对当前文档的处理,这也是为什么建议使用link方式来加载css而不是使用@import。
补充:link和@import的区别
两者都是外部引用CSS的方式,但是存在一定的区别:
区别1:
link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。
区别2:
link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
区别3:
link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
区别4:
ink支持使用Javascript控制DOM去改变样式;而@import不支持。
参考资料
https://blog.csdn.net/binlety/article/details/81448195
面试官:如何用Flex实现三栏等宽布局?
思路:外层容器也就是ul设置display:flex,对项目也就是li设置flex:auto,代表 flex: 1 1 auto
* {
list-style: none;
border: 0;
padding: 0;
margin: 0
}
ul {
width: 500px;
height: 200px;
background: red;
display: flex;
margin: auto;
margin-top: 100px;
padding: 0 10px;
align-items: center;
}
li {
background: green;
height: 100px;
width: 500px;
display: inline-block;
margin: 2px;
line-height: 100px;
text-align: center;
flex: auto
}
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</body>
效果
解析:
我们注意到width的设置,外层ul是500,li也是500,但是实际看到的确实li平分了ul的宽度,这是因为设置了flex:auto,代表有剩余空间的话项目等分剩余空间放大,空间不足项目等比例缩小
面试官:如何实现多维数组扁平化?
题目大意:原数组[[0],[2,3,4],1,[1,[2,3]]],输出[0,2,3,4,1,1,2,3]
//判断当前数组是否有子数组
function hasChildArray(arr) {
return arr.some(element => {
if (Array.isArray(element)) {
has = true;
return true;
}
});
}
let sourceArr = [[0], [2, 3, 4], 1, [1, [2, 3]]];
let result = [];
//递归
(function doFunc(arr) {
if (hasChildArray(arr)) {
for (let i = 0, l = arr.length; i < l; i++) {
if (typeof arr[i] == "number") {
result.push(arr[i]);
} else if (Array.isArray(arr[i])) {
doFunc(arr[i]);
}
}
} else {
result=result.concat(arr);
}
})(sourceArr);
console.log(result);
效果
面试官:ES6的语法特性,如何给一个不懂的人讲symbol,应用场景有哪些?
内容概要
前言
Symbol基础 (老司机可跳过)
使用场景讨论
总结
前言
Symbol是ES6引入的一个新特性 —— 新到什么程度呢?ES5之前是没有任何办法可以模拟Symbol的
但是,我们日常开发工作中,直接使用到Symbol的场景似乎很少。我在网上搜了很多资料,对Symbol开始逐渐加深了理解,接下来就谈谈我的一些看法。
Symbol 基础知识
symbol 是一种全新的基本数据类型 (primitive data type)
Symbol()函数会返回symbol类型的值,作为构造函数来说它并不完整,因为它不支持语法:
new Symbol() // Uncaught TypeError: Symbol is not a constructor
每个从Symbol()返回的symbol值都是唯一的,使用Symbol()创建新的symbol值,并用一个可选的字符串作为其描述 —— 描述相同的两个Symbol值依然是不同的
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo'); //描述
console.log(typeof symbol1); // "symbol"
console.log(symbol2 === 42); // false
console.log(symbol3.toString()); // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo')); // false
一个symbol值能作为对象属性的标识符 —— 这是该数据类型仅有的目的(划重点)
const obj = {};
const myPrivateMethod = Symbol();
obj[myPrivateMethod] = function() {...};
当一个 symbol 类型的值在属性赋值语句中被用作标识符,该属性(像这个 symbol 一样)是匿名的, 并且是不可枚举的 —— 因为这个属性是不可枚举的,它不会在循环结构 for( ... in ...) 中作为成员出现,也因为这个属性是匿名的,它同样不会出现在 Object.getOwnPropertyNames() 的返回数组里。 这个属性可以通过创建时的原始 symbol 值访问到,或者通过遍历 Object.getOwnPropertySymbols() 返回的数组。 在上面的代码示例中,只有通过保存在变量 myPrivateMethod的值可以访问到对象属性(划重点)
const obj = {};
const aProperty = Symbol("a");
obj[aProperty] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";
for (let i in obj) {
console.log(i); // 输出 "c" 和 "d"
}
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(a), Symbol(b) ]
console.log(obj[aProperty]); // a
console.log(obj[Symbol.for("b")]); // b
全局共享的Symbol
JavaScript 有个全局 symbol 注册表
Symbol.for() 参数为字符串
使用给定的key搜索现有的symbol,如果找到则返回该symbol。否则将使用给定的key在全局symbol注册表中创建一个新的symbol
Symbol.keyFor() 参数为Symbol
从全局symbol注册表中,为给定的symbol检索一个共享的key(字符串)
const a = Symbol.for('foo');
const b = Symbol.for('foo');
console.log(a === b); //true
const aKey = Symbol.keyFor(a);
console.log(aKey); //foo
const isEqual = Symbol.keyFor(Symbol.for("tokenString")) === "tokenString";
console.log(isEqual); //true
Symbol 类具有一些静态属性,这类属性只有几个, 即所谓的众所周知的 symbol。
它们是在某些内置对象中找到的某些特定方法属性的 symbol。 暴露出这些 symbol 使得可以直接访问这些行为;这样的访问可能是有用的,例如在定义自定义类的时候。 普遍的 symbol 的例子有:“Symbol.hasInstance”用于 instanceof ,“Symbol.iterator”用于类似数组的对象,“Symbol.search”用于字符串对象
以Symbol.hasInstance为例:
class MyClass {
static [Symbol.hasInstance](lho) {
return Array.isArray(lho);
}
}
console.log([1,2,3] instanceof MyClass); // true
所有众所周知的 symbol 列表(本文不详细介绍,有兴趣的自行查阅文档):
Symbol.iterator
Symbol.asyncIterator
Symbol.match
Symbol.replace
Symbol.search
Symbol.split
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.unscopables
Symbol.species
Symbol.toPrimitive
Symbol.toStringTag
(对这些众所周知的Symbol有点困惑?不要紧,本文后面会进一步阐述这些的使用场景)
Symbol 的使用场景
从上面的 Symbol 基础知识,我们知道有且仅有 2 种途径可以创建 Symbol 值 —— Symbol() 函数 和 Symbol.for() 方法
使用 Symbol() 函数创建出来的 Symbol 是独一无二的(参数字符串对此毫不影响)
for (let i = 0; i < 100; i++) {
Symbol('test')
}
即 上面代码创建的100个Symbol值都互不相同
这种唯一性在某些定义常量的场景带来了极大的便利
使用场景一:定义常量
假设你正在开发一个日志记录模块,你希望提供 DEBUG,INFO,WARN 三种级别的日志
log.levels = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');
在上面代码中,其实我们并不关心 DEBUG,INFO,WARN 的值的意义,我们仅仅是想获得三个唯一的值作为标志而已
假如我们使用数字来代替上面的Symbol
log.levels = {
DEBUG: 1,
INFO: 2,
WARN: 3,
};
在新增一个 ERROR级别的时候,我们还要小心不能跟之前的值(1,2,3中的任意一个)相同;或者当我们手滑不小心,把 INFO 的值写成 1 时,DEBUG 和 INFO 的日志就混淆在一起了 —— 这些麻烦的根源在于 number 的值并不是独一无二的
(p.s. Symbol 在这边的作用有点类似于Java中 的 Enum )
使用场景二:在对象中存放自定义元数据
对象中的元数据可以理解为 —— 相对次要的,不影响对象内容的特殊属性。
对象的主要内容和属性是那些可以由 Object.getOwnProperties() 获取到的属性。
自定义元数据可以
给目标对象添加标记,以便做特殊处理 作为对象内部的一个“私有”属性,例:
const size = Symbol('size');
class Collection {
constructor() {
this[size] = 0;
}
add(item) {
this[this[size]] = item;
this[size]++;
}
static sizeOf(instance) {
return instance[size];
}
}
const x = new Collection();
console.log(Collection.sizeOf(x) === 0); // true
x.add('foo');
console.log(Collection.sizeOf(x) === 1); // true
console.log(Object.keys(x)); //['0']
console.log(Object.getOwnPropertyNames(x)); // ['0']
console.log(Object.getOwnPropertySymbols(x)); // [ Symbol(size) ]
( 当然,上面这个例子完全可以使用 function 和 闭包实现真正的私有变量,并且我个人也推荐这么做;这边的代码仅做Symbol的一个使用例子)
提醒:由于Symbol对应的属性是不可遍历的,因此 JSON.stringfy 也不会碰这些元数据。
使用场景三:在工具库开发中埋下hook(钩子函数)接入点
仍然以日志库为例,我们希望用户调用我们的 log 方法时可以自定义某些特殊对象的打印格式。
import console from 'my-console-lib';
const inspect = console.Symbols.INSPECT; // 由我们的库导出的一个Symbol值
const myVeryOwnObject = {};
console.log(myVeryOwnObject); // 输出 `{}`
myVeryOwnObject[inspect] = function () { return 'DUUUDE'; }; // 用户可以给目标对象设置hook函数
console.log(myVeryOwnObject); // 输出 `DUUUDE` -- 优先执行用户的hook
我们的 log 方法可以这么实现
console.log = function (...items) {
let output = '';
for(const item of items) {
if (typeof item[console.Symbols.INSPECT] === 'function') {
output += item[console.Symbols.INSPECT](item);
} else {
output += console.inspect[typeof item](item);
}
output += ' ';
}
process.stdout.write(output + '\n');
}
举个真实的例子:
在redux的源码中(github链接)
[Symbol.observable](): Observable<S>
使用了 Symbol.observable 这么一个 Symbol 作为 observable/reactive 库的接入口。
而 Symbol.observable 来自于一个规范 https://github.com/tc39/proposal-observable ,有兴趣的读者可以进一步研读。
使用场景四:类似lodash的工具库
上面 Symbol基础章节中提到了“众所周知的Symbol”,它们通常可以用来定制对象的一些行为。
在日常开发中,其实我们很少能接触到这类需求。但是,在类似lodash这种工具库中,为了能做到广泛的适用性,就需要检查目标对象是否定义了这些特殊的Symbol,同时也要依据规范在返回的对象中设置好这些Symbol对应的属性。
举个例子: lodash 的 toArray.js
/** Built-in value references. */
const symIterator = Symbol.iterator // 众所周知的Symbol
function toArray(value) {
if (!value) {
return []
}
if (isArrayLike(value)) {
return isString(value) ? stringToArray(value) : copyArray(value)
}
if (symIterator && value[symIterator]) { // 如果value包含这个Symbol,就利用这个Symbol遍历value的值
return iteratorToArray(value[symIterator]())
}
const tag = getTag(value)
const func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values)
return func(value)
}
export default toArray
除非你的工作项目就是这类库,否则对这些特殊的Symbol只需要简单了解就足够了(个人看法)。
总结
Symbol 是 ES6 引入的一个全新的基础数据类型,它只拥有一些少量且简单的特性。
在合适的场景下,Symbol能发挥出更高效且灵活的作用。
欢迎分享你对Symbol的理解和使用场景,如果觉得这篇文章对你有帮助,记得一键三连!
参考资料
https://zhuanlan.zhihu.com/p/183874695
面试官:字节面试官:requestAnimationFrame 是宏任务还是微任务?
宏任务和微任务
微任务:
process.nextTick
MutationObserver
Promise.then catch finally
宏任务:
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame
从上面可以看出宏任务和微任务的区别
宏任务:
是没有使用回调,且又不按照代码的执行顺序执行的任务
微任务:
首先他也是不按照代码顺序执行的,但是他有回调,比如外面promise,我们可以反复掉用then
参考资料
https://blog.csdn.net/weixin_45581741/article/details/108737474
https://zhuanlan.zhihu.com/p/360507457
面试官:使用React hooks如何只让下面这段代码的子组件只render一次?
>代码大致如下,子组件有自己的状态但受父组件的控制,当父组件count更新的时候,需要将子组件的number和父组件的count保持同步,但是使用useEffect和useMemo都会让子组件render两次,有什么方法可以只render一次吗?
import React, { useEffect, useMemo, useState } from 'react'
function A() {
const [count, setCount] = useState(0)
return <div>
<p>我是父组件</p>
<p>父组件的count是{count}</p>
<button onClick={() => setCount(count + 1)}>click</button>
<B count={count} />
</div>
}
const B = React.memo(({count}: {count: number}) => {
const [number, setNumber] = useState(0)
useMemo(() => {
setNumber(count)
}, [count])
console.log('子组件render')
return <div>
<p>我是子组件</p>
<p>子组件的number是{number}</p>
<button onClick={() => setNumber(number + 1)}>click</button>
</div>
})
export default A
解析
import React, { useEffect, useMemo, useState, useRef } from 'react';
function A() {
const [count, setCount] = useState(0);
return (
<div>
<p>我是父组件</p>
<p>父组件的count是{count}</p>
<button onClick={() => setCount(count + 1)}>click</button>
<B count={count} />
</div>
);
}
const B = React.memo(({ count }: { count: number }) => {
const numberRef = useRef(0);
const [, update] = useState({});
const updateNumber = () => {
numberRef.current++;
update({});
};
useMemo(() => {
numberRef.current = count;
}, [count]);
console.log('子组件render');
return (
<div>
<p>我是子组件</p>
<p>子组件的number是{numberRef.current}</p>
<button onClick={updateNumber}>click</button>
</div>
);
});
使用useRef保存子组件状态,当父组件更新时,直接更新ref值;当子组件click时,在更新ref值后,再调用一次update触发子组件渲染。
在线示例:
https://codesandbox.io/s/headless-dawn-vvyu9
参考资料
https://www.zhihu.com/people/qiqiboy-81
https://www.zhihu.com/question/444068787
https://www.zhihu.com/question/444068787/answer/1739616037
------------------
FrontendPeople/套卷/每日一题面试题/1.谈谈Vue和React的区别?.md
43 lines (38 sloc) 3 KB
面试题专栏说明
最近刚好换了工作,把最近面试,被面的问题,以及现在大多数出现的面试题以一个专题的形式一次性总结,一天一题,现在总共汇总了150道题,感觉如果这些题都能答出来25k稳如老狗了,每道题目答案没有多余扯皮的部分,就是单纯的答案。
谈谈Vue和React的区别?
1.数据流的不同
组件与DOM之间可以通过 v-model 双向绑定(双向数据流)
React单向数据响应,需要手动setState
2.监听数据变化的实现原理不同
Vue通过 getter/setter
React默认是通过比较引用的方式(diff)进行的
3.代码的复用
Vue是才有公共组件或者mixin的方式实现代码的复用
React是通过 HoC (高阶组件)实现代码的复用
4.组件通信的区别
Vue是通过props、$emit/$on、$parent/$children、EventBus、vuex、$root等方式实现组件通信
React可以通过props向子组件传递数据或者回调或者context实现组件通信
5.模板渲染方式的不同
Vue是通过一种拓展的HTML语法进行渲染(其实Vue也是可以使用JSX) Vue是在和组件JS代码分离的单独的模板中,通过指令来实现的,比如条件语句就需要 v-if 来实现
React 是通过JSX渲染模板
React是在组件JS代码中,通过原生JS实现模板中的常见语法,比如插值,条件,循环等,都是通过JS语法实现的
6.diff算法不同
vue比对节点,当节点元素类型相同,但是className不同,认为是不同类型元素,删除重建,而react会认为是同类型节点,只是修改节点属性
vue的列表比对,采用从两端到中间的比对方式,而react则采用从左到右依次比对的方式。当一个集合,只是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移动到第一个。总体上,vue的对比方式更高效。
7.Vuex 和 Redux 的区别
Vuex 更加灵活一些,组件中既可以 dispatch action 也可以 commit updates,而 Redux 中只能进行 dispatch,并不能直接调用 reducer 进行修改
Redux 使用的是不可变数据,而Vuex的数据是可变的。Redux每次都是用新的state替换旧的state,而Vuex是直接修改
Redux 在检测数据变化的时候,是通过 diff 的方式比较差异的,而Vuex其实和Vue的原理一样,是通过 getter/setter来比较的
8.数据声明和使用的方式不同
vue中的数据由data属性在Vue对象中进行管理,react中的数据由state属性管理;
vue通过slot插槽进行嵌套传递,react通过“props.children”的方式将标签内的部分传递给子组件。
说明
每天一到面试题,25k+稳如老狗,点击↓关注【鬼哥】我 当前进度【#001题】
参考资料
https://cnblogs.com/mengff/p/12828825.html
https://zhuanlan.zhihu.com/p/43494278
https://m.php.cn/article/464234.html
https://segmentfault.com/a/1190000019208626
-------------
10.typescript和Es6的class区别.md
73 lines (63 sloc) 2 KB
TypeScript是ES6的超集。至于需不需要使用,在于你所需要的场景。
相同点
都是采用extends语法进行继承
在constructor中都需要首先使用super()调用父类构造函数,然后才能获取父类的属性
最终都是通过ES5的原型链进行继承
不同点
typescript class中有属性字段有private、protected、readonly等修饰符
typescript constructor构造函数参数必须定义所属类型
typescript class中可以存着静态方法
typescript class方法参数会类型验证
es6中, 只有静态方法,没有静态属性。
public 共有的。 类的内外都可以使用。
protected 受保护的。 类的内部使用,继承的子类中使用。
private 私有的。 类的内部使用。
ES6
class UserBase {
constructor (color) {
this.color = color;
}
static showColor () {
//color ==>undefined
console.log(`我是一个有颜色的公有静态函数,看看我的颜色${this.color}`)
}
}
class User extends UserBase {
constructor (userName, userAge) {
super("yellow");
this.userName=userName;
this.userAge=userAge;
}
showInfo(){
console.log(`名字${this.userName}今年:${this.userAge}`)
}
}
const user=new User("鬼鬼",18);
user.showInfo();//实例函数
User.showColor()//静态函数
typescript
class UserBase{
static color="yellow";//静态属性
static showColor() {//静态方法
console.log(`我是一个有颜色的公有静态函数,看看我的颜色${this.color}`)
}
}
class User extends UserBase{
private userName: string;
private userAge: number;
constructor(userName : string,userAge:number){
super();//必须调用super才能继承showColor函数
this.userName=userName;
this.userAge=userAge;
}
private showInfo ():void{
console.log(`名字${this.userName}今年:${this.userAge}`)
}
}
const user=new User("鬼鬼",18);
user.showInfo();//实例函数
User.showColor();//静态函数
----------------------
2.new的实现原理.md
41 lines (37 sloc) 1.03 KB
new实现原理
1、创建一个空对象 obj
2、将该对象 obj 的原型链 proto 指向构造函数的原型 prototype,
并且在原型链 proto 上设置 构造函数 constructor 为要实例化的 Fn
3、传入参数,并让 构造函数 Fn 改变指向到 obj,并执行
4、最后返回 obj
例子
类对象
function User(userAge, userName) {
this.userAge = userAge
this.userName = userName
}
User.prototype.showInfo = function() {
console.log('this.userAge :', this.userAge)
console.log('this.userName :', this.userName)
}
模拟new运算符功能函数
function myNew() {
let obj = {}
let arg = Array.prototype.slice.call(arguments, 1)
obj.__proto__ = Fn.prototype
obj.__proto__.constructor = Fn
Fn.apply(obj, arg)
return obj
};
测试
const user = myNew(User,18, '鬼鬼')
参考资料
https://boxuegu.com/ask/detail/12985
https://dazhuanlan.com/2020/02/03/5e37df6bd16b8/
https://cnblogs.com/linjunfu/p/10791467.html
--------------------------------
3.从浏览器地址栏输入 URL 到显示的过程是怎么实现的?.md
30 lines (29 sloc) 1.8 KB
DNS解析,将域名地址解析为ip地址
浏览器DNS缓存
系统DNS缓存
路由器DNS缓存
运营商DNS缓存
再找不到就递归搜索该网址
如果找到了,就会 TCP连接:TCP三次握手
第一次握手,由浏览器发起,告诉服务器,我要发送请求了
第二次握手,由服务器发起,告诉浏览器我准备接受了,你发送吧
第三次握手,由浏览器发送,告诉服务器,我马上发送了,准备接受吧
发送请求报文
发送HTTP协议的通信内容,即请求报
接受响应
响应报文
渲染页面
遇见HTML标记,浏览器调用HTML解析器解析成Token,并构建成dom树
遇见style/link标记,浏览器调用css解析器
遇见script标记,调用javascript解析器,处理script代码(绑定时间,修改dom树/css dom树)
根据渲染树计算布局,计算每个节点的几何信息(布局)
将各个节点颜色绘制到屏幕上(渲染) 注意: 这5个步骤不一定按照顺序执行,如果dom树或cssdom树被修改了,可能会执行多次布局和渲染 往往实际页面中,这些步骤都会执行多次。
断开连接:TCP的四次挥手
第一次挥手,浏览器发起,发送给服务器,我东西发送完了(请求报文),你准备关闭吧
第二次挥手,服务器发起,告诉浏览器,我东西接收完了(请求报文),我准备关闭了,你也准备吧
第三次挥手,服务器发起,告诉浏览器,我东西发送完了(响应报文),你准备关闭吧
第四次挥手,浏览器发起,告诉服务器,我东西接收完了, 我准备关闭了,你也准备吧
参考资料
http://r4v.cn/gqk4o
https://zhuanlan.zhihu.com/p/260125085
https://blog.csdn.net/xiaohu_Blog/article/details/109521828
---------------------------
4.JS有哪些方式可以实现继承.md
144 lines (129 sloc) 3.3 KB
继承的含义:
继承是面向对象编程中的一个重要概念,通过继承可以使子类的实例拥有在父类中定义的属性和方法。
1、原型链继承
function UserBase(){
}
function User(){
}
User.prototype = new UserBase();
将父类的实例作为子类的原型。
(1)不能向构造函数传参,无法实现多继承
(2)来自原型对象的引用属性是所有实例共享的
2、构造继承
实际上使用父类的构造函数来增强子类,等于是把父类的构造函数复制给子类。
function UserBase(){
}
function User(userName) {
UserBase.call(this);
this.userName = userName;
}
let user = new User("鬼鬼")
user.userName;
优点:
(1)可以向构造函数传参数
(2)可以实现多继承,多call几个
缺点:
(1)无法实现函数复用
(2)只能继承父类的属性和方法,不能继承父类的原型
3、实例继承
为父类实例添加新属性,作为子类实例返回。
function UserBase(){
}
function User(userName) {
let userBase = new UserBase();
userBase.userName = userName;
return userBase;
}
let user = new User("鬼鬼")
user.userName;
缺点:无法实现多继承
4、拷贝继承
function UserBase(userName){
}
UserBase.prototype.showInfo = function(){
console.log(this.userName)
}
function User(userName) {
let userBase = new UserBase();
for (let attr in userBase) {
User.prototype[attr] = userBase[attr];
}
this.userName = userName;
}
let user = new User("鬼鬼")
user.showInfo();
优点:支持多继承
缺点:占用内存高,因为要用for in循环来拷贝父类属性/方法
不可枚举方法拷贝不了
5、组合继承
通过调用父类构造函数,继承了父类的属性,并保留了传参的优点。
然后再将父类实例作为子类原型,实现了函数复用。
function UserBase(userName){
this.userName = userName
}
UserBase.prototype.showInfo = function(){
console.log(this.userName)
}
function User (userName){
//call方式
UserBase.call(this,userName)
//apply方式
UserBase.apply(this,[userName])
}
User.prototype = new UserBase()
let user = new User("鬼鬼")
user.showInfo();
优点:
(1)继承父类的属性和方法,也继承了父类的原型
(2)可传参,函数可复用
缺点:
调用了两次父类构造函数
6、寄生组合继承
通过寄生的方式,去掉了父类的实例属性,在调用父类构造函数时,
就不会初始化两次实例方法,避免了组合继承的缺点
function UserBase(userName){
this.userName = userName
}
UserBase.prototype.showInfo = function(){
console.log(this.userName)
}
function User (userName){
UserBase.call(this,userName)
}
User.prototype = Object.create(UserBase.prototype)
User.prototype.constructor = User
let user = new User("鬼鬼")
user.showInfo();
7、Class继承
class UserBase{
constructor(userName){
this.userName = userName
}
showInfo(){
console.log(this.userName)
}
}
class User extends UserBase{
constructor(value){
super(value)
}
}
var user = new User("鬼鬼")
user.showInfo();
参考资料
https://blog.csdn.net/guoqing2016/article/details/106418081/
http://www.bubuko.com/infodetail-2556919.html
----------------
5.JS为什么会有原型的概念.md
55 lines (32 sloc) 3.09 KB
今天的每日一题还是非常有意思的,当一个故事来读吧,如果是面试场景的话,也可以直接回答文章最后总结的两点。
因为早期的浏览器只能用来浏览,不具备与访问者互动的能力。比如,如果网页上有一栏"用户名"要求填写,浏览器就无法判断访问者是否真的填写了,只有让服务器端判断。
如果没有填写,服务器端就返回错误,要求用户重新填写,这太浪费时间和服务器资源了。
这个时候需要一门网页脚本语言,这种脚本语言能够完成一些简单的操作,比如判断用户有没有填写表单。刚好这个时候是向对象编程(object-oriented programming)最兴盛的时期,C++是当时最流行的语言,而Java语言也马上推出。
所以Javascript作者也受到了启发,Javascript里面所有的数据类型都是对象(object),这一点与Java非常相似。但是直接使用java的"继承"机制来实现,又觉得过于笨重,但是,Javascript里面都是对象,必须有一种机制,将所有对象联系起来。所以,javascript作者最后还是设计了"继承"。
但是,他不打算引入"类"(class)的概念,因为一旦有了"类",Javascript就是一种完整的面向对象编程语言了,这好像有点太正式了,而且增加了初学者的入门难度。
他考虑到,C++和Java语言都使用new命令,生成实例。
C++的写法是:
ClassName *object = new ClassName(param);
Java的写法是:
Foo foo = new Foo();
这时,他想到C++和Java使用new命令时,都会调用"类"的构造函数(constructor)。他就做了一个简化的设计,在Javascript语言中,new命令后面跟的不是类,而是构造函数。
但是很快发现用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。
每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。
最终加入了prototype属性的引入
考虑到这一点,作者决定为构造函数设置一个prototype对象属性。
所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
实例对象一旦创建,将自动引用prototype对象的属性和方法。
由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象一样。
面试总结回答
JavaScript采用原型编程,所有对象都能共享原型上的方法,节省内存;
同时基于原型这一实现思想,JavaScript通过找对原型链,方便地实现了继承。
这就是原型编程带来的2个最大好处!!!
内容说明
内容有最简化,如果需要看原始总结请查看阮一峰博客↓
参考资料
http://ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html
https://blog.csdn.net/daigualu/article/details/54772799
-------------------------------------------
6.什么是原型链.md
54 lines (40 sloc) 1.42 KB
说明
今天的内容比较简单,但是也希望大家做个知识回顾。
原型链概念
原型链的核心就是依赖对象的_proto_的指向,当自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到Object时,就没有_proto_指向了。
比如我建了一个对象
function User(userName,userAge){
this.userName=userName;
this.userAge=userAge;
this.toString=()=>{
return `${this.userName}永远${this.userAge}`
}
}
var user = new User("鬼鬼",18);
user.toString()
流程说明
当我们「读取」 user.toString 时,JS 引擎会做下面的事情:
首先查找user 对象本身有没有 toString 方法属性。
因为的定义了,所以会输出
"鬼鬼永远18"
如果我把代码改成
function User(userName,userAge){
this.userName=userName;
this.userAge=userAge;
}
var user = new User("鬼鬼",18);
user.toString()
如果没有就继续查看user.proto 对象有没有 toString 方法属性
因为没有,没有就继续向上查找:user.proto.proto 对象有没有 toString 方法属性
因为user.proto.proto 有toString方法,这个方法为Object对象默认的,所以输出:
"[object Object]"
这个查找过程会一直持续到:找到 toString方法属性 或者 proto 为 null才会结束
就这个链式的查找的过程就叫做【原型链】
--------------------------
7.vuex使用流程是什么样的.md
137 lines (118 sloc) 2.86 KB
这道题在Vue面试岗位还是问的挺多的
一、使用Vuex的目的
实现多组件状态管理,多个组件之间需要数据共享
二、Vuex 的五大核心
其中state和mutation是必须的,其他可根据需求来加
1、state 负责状态管理,类似于vue中的data,用于初始化数据
2、mutation 专用于修改state中的数据,通过commit触发
3、action 可以处理异步,通过dispatch触发,不能直接修改state,首先在组件中通过dispatch触发action,然后在action函数内部commit触发mutation,通过mutation修改state状态值
4、getter Vuex中的计算属性,相当于vue中的computed,依赖于state状态值,状态值一旦改变,getter会重新计算,也就是说,当一个数据依赖于另一个数据发生变化时,就要使用getter
5、module 模块化管理
使用流程
1.创建store实例并且导出 store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
//声明state
state: {
userInfo:{ userName:"" }
},
//声明mutations
mutations: {
setUserInfo(state,userInfo){
state.userInfo = userInfo
}
},
//声明actions
actions: {
setUserInfo({ commit },userInfo){
commit('setUserInfo',userInfo)
}
},
//声明getters
getters:{
userName(state){
return "姓名:"+state.userInfo.userName
}
}
})
export default store
2.引入Vuex
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
render: h => h(App),
store
}).$mount('#app')
3.组件内使用
使用方式一
<template>
<div>
<!-- state使用 -->
<p>{{$store.state.userInfo.userName}}</p>
<!-- getters使用 -->
<p>{{$store.getters.userName}}</p>
<!-- action使用 -->
<p @click="setInfo">存储信息</p>
</div>
</template>
export default {
methods: {
setInfo(){
this.$store.commit('setUserInfo',{
userName:"鬼鬼"
})
}
}
}
使用方式二
<template>
<div>
<!-- state使用 -->
<p>{{userInfo.userName}}</p>
<!-- getters使用 -->
<p>{{userName}}</p>
<!-- action使用 -->
<p @click="setInfo">存储信息</p>
</div>
</template>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
methods: {
...mapActions(['setUserInfo']),
// ...mapMutations(["setUserInfo"]),
setInfo(){
this.setUserInfo({
userName:"鬼鬼"
})
},
computed: {
...mapState({
userInfo: state => state.userInfo
}),
...mapGetters(['userName']),
}
}
参考资料
https://blog.csdn.net/u012967771/article/details/114132555
https://cnblogs.com/leslie1943/p/13370639.html?utm_source=tuicool
Footer
-----------------------
8.说说你常用的css3属性有哪些.md
62 lines (61 sloc) 1.74 KB
1.CSS3边框:
border-radius:CSS3圆角边框。
box-shadow:CSS3边框阴影。
border-image:CSS3边框图片。
2.CSS3背景:
background-size: 属性规定背景图片的尺寸。
background-origin :属性规定背景图片的定位区域。
background-clip:指定背景图片从什么位置开始裁剪
3.CSS3文字效果:
text-shadow:在 CSS3 中,text-shadow 可向文本应用阴影。
word-wrap :单词太长的话就可能无法超出某个区域,允许对长单词进行拆分,并换行到下一行
4.CSS3 2D转换:
transform:通过 CSS3 转换,我们能够对元素进行移动、缩放、转动、拉长或拉伸。
translate():元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标)
transform:translate(50px,100px)
rotate():实现元素的旋转效果
transform:rotate(7deg);
scale():实现元素的缩放效果
transform:scale(1.1,1.1);
skew():实现元素的倾斜效果
transform:skew(10deg,10deg);
matrix(): 方法需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素。
transform: matrix(3, 0, 0, 0.5, 0, 0);
clip-path:clip-path里面的polygon功能,我们可以通过它来绘制多边形
-webkit-clip-path: polygon(0 100%, 50% 0, 100% 100%);
5.CSS3 过渡
transition
transition: width 2s;
6.CSS3 动画
animation
animation:myAnimation 5s infinite;
选择器
div:nth-last-child(n)
div:nth-of-type(n)
div:nth-last-of-type(n)
div:last-child
div:first-of-type
div:only-child
div:only-of-type
div:empty
div:checked
div:enabled
div:disabled
div::selection
div:not(s)
Footer
---------------------
9.forEach()和map()的区别.md
48 lines (42 sloc) 1.16 KB
相同点:
都是循环遍历数组中的每一项
每次执行匿名函数参数一样,分别是item(当前每一项)、index(索引值)、arr(原数组)
只能遍历数组
匿名函数中的this都是指向window;
两种方法都不能用 break 中断
const users = ["鬼鬼", "刘亦菲", "周星驰"];
users.forEach((item, index, arr) => {
console.log(item, index, arr);
},this);
const users = ["鬼鬼", "刘亦菲", "周星驰"];
users.map((item,index,arr)=>{
console.log(item,index,arr)
},this)
不同点:
map()速度比forEach()快;
map()会返回一个新数组,不对原数组产生影响;
map()方法输出可以与其他方法(如reduce()、sort()、filter())链接在一起
性能对比
const users=new Array(10000).fill("鬼鬼");
// 1. forEach()
console.time("forEach");
const newArray = [];
users.forEach((item) => {
newArray.push(item)
});
console.timeEnd("forEach");
// 2. map()
console.time("map");
const newArray2 = users.map ((item) => {
return item
});
console.timeEnd("map");
差距还是挺大的
参考资料
https:/cnblogs.com/kaiqinzhang/p/11496151.html
-------------------
10.typescript和Es6的class区别.md
73 lines (63 sloc) 2 KB
TypeScript是ES6的超集。至于需不需要使用,在于你所需要的场景。
相同点
都是采用extends语法进行继承
在constructor中都需要首先使用super()调用父类构造函数,然后才能获取父类的属性
最终都是通过ES5的原型链进行继承
不同点
typescript class中有属性字段有private、protected、readonly等修饰符
typescript constructor构造函数参数必须定义所属类型
typescript class中可以存着静态方法
typescript class方法参数会类型验证
es6中, 只有静态方法,没有静态属性。
public 共有的。 类的内外都可以使用。
protected 受保护的。 类的内部使用,继承的子类中使用。
private 私有的。 类的内部使用。
ES6
class UserBase {
constructor (color) {
this.color = color;
}
static showColor () {
//color ==>undefined
console.log(`我是一个有颜色的公有静态函数,看看我的颜色${this.color}`)
}
}
class User extends UserBase {
constructor (userName, userAge) {
super("yellow");
this.userName=userName;
this.userAge=userAge;
}
showInfo(){
console.log(`名字${this.userName}今年:${this.userAge}`)
}
}
const user=new User("鬼鬼",18);
user.showInfo();//实例函数
User.showColor()//静态函数
typescript
class UserBase{
static color="yellow";//静态属性
static showColor() {//静态方法
console.log(`我是一个有颜色的公有静态函数,看看我的颜色${this.color}`)
}
}
class User extends UserBase{
private userName: string;
private userAge: number;
constructor(userName : string,userAge:number){
super();//必须调用super才能继承showColor函数
this.userName=userName;
this.userAge=userAge;
}
private showInfo ():void{
console.log(`名字${this.userName}今年:${this.userAge}`)
}
}
const user=new User("鬼鬼",18);
user.showInfo();//实例函数
User.showColor();//静态函数
Footer
-------------------
11.methods,computed,watch三者区别.md
66 lines (64 sloc) 2.02 KB
watch
当数据改变时,直接触发对应操作;
可以监听的数据有三部分,即props,data,computed;
所触发的对应操作函数中包含两个参数,第一个参数是新值newValue,第二个参数是旧值oldValue;
不支持缓存,但支持异步,在数据变化时执行异步或开销较大的操作时使用;
一对多,即一个数据改变时,可以通过触发对应操作改变多个其他的数据。
computed
computed 依赖于 data 中的数据,只要在它的相关依赖发生改变时就会触发computed;
计算属性是基于属性的依赖进行缓存的,当data中的数据未变时,优先取缓存中的东西;
支持缓存,但不支持异步;
多对一或一对一,即一个属性是由其他属性计算而来的,这个属性可能是依赖其他一个或多个属性。
methods
是一个方法,执行的时候需要事件进行触发;
可以在模板或者js中以方法的形式调用
执行次数跟调用次数想对应
使用说明
export default {
data: {
userName: '鬼鬼'
},
watch:{
userName(newValue,oldValue){
console.log(`原来的值:${newValue}`);
console.log(`新的值${oldValue}`);
},
//深度监听
userName:{
handler:function(newValue,oldValue){
console.log(`原来的值:${newValue}`);
console.log(`新的值${oldValue}`);
},
immediate:true,
deep:true
},
},
computed: {
gUserName() {
return this.userName
},
//computed传参
gUserName(keyName){
return function(keyName){
return keyName+this.userName
}
}
},
methods: {
getUserName() {
return this.userName
}
}
}
<template>
<!-- methods -->
<div> 我的名字叫:{{ getUserName() }} </div>
<!-- computed -->
<div> 我的名字叫:{{ gUserName }} </div>
<!-- computed 传参 -->
<div> {{ gUserName("我的名字叫:") }} </div>
</template>
---------------
12.tcp和udp的区别.md
63 lines (46 sloc) 3.72 KB
区别
区别一、是否基于连接
TCP是面向连接的协议,而UDP是无连接的协议。
即TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接。
区别二、可靠性 和 有序性 区别
TCP 提供交付保证(Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输),无差错,不丢失,不重复,且按序到达,也保证了消息的有序性。该消息将以从服务器端发出的同样的顺序发送到客户端,尽管这些消息到网络的另一端时可能是无序的。TCP协议将会为你排好序。
UDP不提供任何有序性或序列性的保证。UDP尽最大努力交付,数据包将以任何可能的顺序到达。
TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
区别三、实时性
UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
区别四、协议首部大小
TCP首部开销20字节; UDP的首部开销小,只有8个字节 。
区别五、运行速度
TCP速度比较慢,而UDP速度比较快,因为TCP必须创建连接,以保证消息的可靠交付和有序性,毕竟TCP协议比UDP复杂。
区别六、拥塞机制
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
区别七、流模式(TCP)与数据报模式(UDP);
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;
UDP是面向报文的 。
区别八、资源占用
TCP对系统资源要求较多,UDP对系统资源要求较少。
TCP被认为是重量级的协议,而与之相比,UDP协议则是一个轻量级的协议。因为UDP传输的信息中不承担任何间接创造连接,保证交货或秩序的的信息。这也反映在用于承载元数据的头的大小。
区别九、应用
每一条TCP连接只能是点到点的;
UDP支持一对一,一对多,多对一和多对多的交互通信 。
于UDP不需要建立连接,所以且适合多播的环境,UDP是大量使用在游戏和娱乐场所。
优点
TCP和UDP的优缺点也很明显了。UDP 优点:简单、传输快。
网速的提升给UDP的稳定性提供可靠网络保障,丢包率很低,如果使用应用层重传,能够确保传输的可靠性。
TCP为了实现网络通信的可靠性,使用了复杂的拥塞控制算法,建立了繁琐的握手过程,由于TCP内置的系统协议栈中,极难对其进行改进。采用TCP,一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接收到后再继续发送,延时会越来越大,基于UDP对实时性要求较为严格的情况下,采用自定义重传机制,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成影响。
缺点
不可靠,不稳定;
UDP应用场景:
面向数据报方式
网络数据大多为短消息
拥有大量Client
对数据安全性无特殊要求
网络负担非常重,但对响应速度要求高
TCP:
优点:可靠 稳定
TCP的可靠体现在TCP在传输数据之前,会有三次握手来建立连接,而且在数据传递时,有确认. 窗口. 重传. 拥塞控制机制,在数据传完之后,还会断开来连接用来节约系统资源。
缺点:慢,效率低,占用系统资源高,易被攻击
TCP应用场景:
当对网络质量有要求时,比如HTTP,HTTPS,FTP等传输文件的协议;POP,SMTP等邮件传输的协议。
原文地址:
https://blog.csdn.net/weixin_39789689/article/details/82560805
------------------------
虾皮一卷01.md
181 lines (143 sloc) 5.79 KB
js数据类型的检测方式,基本数据类型有哪些,null的数据类型是什么,(object),为什么是object?
js数据类型的检测方式
1. typeof
typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。 返回的结果用该类型的字符串(全小写字母)形式表示
包括以下 7 种:
number、boolean、symbol、string、object、undefined、function 等
typeof对于基本数据类型的判断是没有问题的,但是遇到了引用数据类型是不起作用的,只能返回一个object。(typeof(null)输出object 是因为在js中,null表示一个空对象指针)
对于基本类型,除 null 以外,均可以返回正确的结果。
对于 null ,返回 object 类型。
对于 function 返回 function 类型。
2、instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。
在这里需要特别注意的是:instanceof 检测的是原型,我们用一段伪代码来模拟其内部执行过程:
instanceof (A,B) = {
var L = A.__proto__;
var R = B.prototype;
if(L === R) {
// A的内部属性 __proto__ 指向 B 的原型对象
return true;
}
return false;
}
3、constructor 先看一下用法:
console.log(("1").constructor === String);
console.log((1).constructor === Number);
console.log((true).constructor === Boolean);
console.log(([]).constructor === Array);
console.log((function() {}).constructor === Function);
console.log(({}).constructor === Object);
输出结果:
true
true
true
true
true
true
撇去null和undefined,似乎说constructor能用于检测js的基本类型和引用类型。但当涉及到原型和继承的时候,便出现了问题,如下:
function fun() {};
fun.prototype = new Array();
let f = new fun();
console.log(f.constructor===fun);
console.log(f.constructor===Array);
撇去null和undefined,constructor能用于检测js的基本类型和引用类型,但当对象的原型更改之后,constructor便失效了。
从上述过程可以看出,当 A 的 proto 指向 B 的 prototype 时,就认为 A 就是 B 的实例
4.Object.prototype.toString.call()
用法:
var test = Object.prototype.toString;
console.log(test.call("str"));
console.log(test.call(1));
console.log(test.call(true));
console.log(test.call(null));
console.log(test.call(undefined));
console.log(test.call([]));
console.log(test.call(function() {}));
console.log(test.call({}));
结果:
[object String]
[object Number]
[object Boolean]
[object Null]
[object Undefined]
[object Array]
[object Function]
[object Object]
这样一看,似乎能满足js的所有数据类型,那我们看下继承之后是否能检测出来
function fun() {};
fun.prototype = new Array();
let f = new fun();
console.log(Object.prototype.toString.call(fun))
console.log(Object.prototype.toString.call(f))
结果:
[object Function]
[object Object]
可以看出,Object.prototype.toString.call()可用于检测js所有的数据类型。
js基本数据类型
js有5种基本数据类型: number、boolean、undefined、null、string
js null是什么类型?
null:是 Null类型,表示一个 空对象指针 或 尚未存在的对象
即该处不应该有值,使用typeof运算得到 object ,是个特殊对象值,转为数值为 0。
也可以理解是表示程序级的、正常的或在意料之中的值的空缺
作为函数的参数,表示该函数的参数不是对象
作为对象原型链的终点
注意:
null 不是一个对象,但 typeof null === object 原因是不同的对象在底层都会表示为二进制,在 JS 中如果二进制的前三位都为 0,就会被判断为object类型,null 的二进制全为 0,自然前三位也是 0,所以 typeof null === ‘objcet’
undefined:
是Undefined 类型,表示一个 无 的原始值 或 缺少值, 即此处应该有一个值,但还没有定义,使用 typeof undefined === ‘undefined’,转为数值为 NaN。
它是在 ECMAScript 第三版引入的预定义全局变量,为了区分空指针对象 和 未初始化的变量。
也可以理解是表示系统级的、出乎意料的或类似错误的值的空缺
变量被声但没有赋值时
调用函数时,应该提供的参数没有提供时
对象没有赋值的属性时,属性值为 undefined
函数没有返回值时,默认返回值为 undefined
(object),为什么是object
"()"也叫分组运算符,它有两种用法:如果表达式放在圆括号中,作用是求值;如果跟在函数后面,作用是调用函数,把表达式放在圆括号之中,将返回表达式的值
console.log((1)); //1
console.log(('a')); //'a'
console.log((1+2)); // 3
把对象放在圆括号之中,则会返回对象的值,即对象本身
var o = {p:1};
console.log((o));// Object {p: 1}
将函数放在圆括号中,会返回函数本身。如果圆括号紧跟在函数的后面,就表示调用函数,即对函数求值
function f(){return 1;}
console.log((f));// function f(){return 1;}
console.log(f()); // 1
[注意]圆括号运算符不能为空,否则会报错
();//SyntaxError: Unexpected token )
由于圆括号的作用是求值,如果将语句放在圆括号之中,就会报错,因为语句没有返回值
console.log(var a = 1);// SyntaxError: Unexpected token var
console.log((var a = 1));// SyntaxError: Unexpected token var
参考资料
https://segmentfault.com/a/1190000019259209
https://blog.csdn.net/weixin_42878211/article/details/108037109
http://blog.itblood.com/4590.html
https://blog.csdn.net/weixin_34417200/article/details/88861212
----------------------------
虾皮一卷02.md
33 lines (31 sloc) 850 Bytes
2.new的过程发生了什么? 代码实例
function User(userName) {
this.userName = userName;
}
const user = new User("鬼哥");
创建空对象;
var obj = {};
设置新对象的 constructor 属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的 prototype 对象;
obj.__proto__ = User.prototype;
使用新对象调用函数,函数中的 this 被指向新实例对象:
User.call(obj); //{}.构造函数();
如果无返回值或者返回一个非对象值,则将新对象返回;如果返回值是一个新对象的话那么直接直接返回该对象。
if (typeof(result) == "object") {
user = result;
} else {
user = obj;
}
参考资料
https://my.oschina.net/qiilee/blog/4915319
https://developer.aliyun.com/ask/258926
-----------------
虾皮一卷02.md
33 lines (31 sloc) 850 Bytes
2.new的过程发生了什么? 代码实例
function User(userName) {
this.userName = userName;
}
const user = new User("鬼哥");
创建空对象;
var obj = {};
设置新对象的 constructor 属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的 prototype 对象;
obj.__proto__ = User.prototype;
使用新对象调用函数,函数中的 this 被指向新实例对象:
User.call(obj); //{}.构造函数();
如果无返回值或者返回一个非对象值,则将新对象返回;如果返回值是一个新对象的话那么直接直接返回该对象。
if (typeof(result) == "object") {
user = result;
} else {
user = obj;
}
参考资料
https://my.oschina.net/qiilee/blog/4915319
https://developer.aliyun.com/ask/258926
---------------
虾皮一卷03.md
116 lines (96 sloc) 3.96 KB
3.深拷贝 浅拷贝 怎么实现一个深拷贝 出现递归循环怎么办,结束条件是什么?
深拷贝和浅拷贝的区别
深拷贝在于引用类型的时候,浅拷贝只复制地址值,实际上还是指向同一堆内存中的数据,深拷贝则是重新创建了一个相同的数据,二者指向的堆内存的地址值是不同的。这个时候修改赋值前的变量数据不会影响赋值后的变量。
深拷贝实现
1.通过递归方式实现深拷贝
function deepClone(obj) {
var target = {};
for(var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (typeof obj[key] === 'object') {
target[key] = deepClone(obj[key]);
} else {
target[key] = obj[key];
}
}
}
return target;
}
2.通过json的方式实现
function (obj) {
let tmp = JSON.stringify(obj);
let result = JSON.parse(tmp);
return result;
}
3.通过Object.create()实现
function deepCopy(obj) {
var copy = Object.create(Object.getPrototypeOf(obj));
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
}
var obj1 = { a: 1, b: {bc: 50, dc: 100, be: {bea: 1}} };
var obj2 = deepCopy(obj1);
console.log(obj2)
obj1.a = 20;
console.log(obj1)
console.log(obj2)
//Object {a: 1, b: Object}
//Object {a: 20, b: Object}
//Object {a: 1, b: Object}
浅拷贝
如果我们要复制对象的所有属性都不是引用类型时,就可以使用浅拷贝,实现方式就是遍历并复制,最后返回新的对象。
function shallowCopy(obj) {
var copy = {};
// 只复制可遍历的属性
for (key in obj) {
// 只复制本身拥有的属性
if (obj.hasOwnProperty(key)) {
copy[key] = obj[key];
}
}
return copy;
}
如上面所说,我们使用浅拷贝会复制所有引用对象的指针,而不是具体的值,所以使用时一定要明确自己的需求,同时,浅拷贝的实现也是最简单的。
JS内部实现了浅拷贝,如Object.assign(),其中第一个参数是我们最终复制的目标对象,后面的所有参数是我们的即将复制的源对象,支持对象或数组,一般调用的方式为
var newObj = Object.assign({}, originObj);
这样我们就得到了一个新的浅拷贝对象。另外[].slice()方法可以视为数组对象的浅拷贝。
深拷贝出现递归循环怎么办?结束条件是什么?
问题描述
js对象clone是RIA编程中常用方法,但是对象属性之间的循环引用会导致clone的递归进入死循环。
js 代码
var a = {pa1:'av1',pa2:'av2'};
var b = {pb1:'bv1',pb2:'bv2'};
a.pa3 = b;
//b.pb3 = a;
var c = cloneobj(a);
如果不包含注释掉的一行,clone是可以正常进行的。
但是如果引入这一行,即出现了js对象属性的循环引用,clone将进入递归的死循环。
现象
浏览器能够很好的处理这种错误,并抛出“too much recursion"错误,并定位到相应的代码行。 (小声说一句:如果写个js死循环就把浏览器搞死了,B/S应用就没法混了)
办法
如何解决了,通常的办法就是限制递归的深度,例如DWR的‘DWRUtil.toDescriptiveString’
但是窃以为这个并非好的解决办法,因为了对应用造成极大的限制。
我的办法就是,在clone的过程中,记住每个已经clone的对象属性, 并且在对对象进行深度clone之前,首先检查是否已经clone过了,如果是,则返回已clone的引用即可。
因此只要放开示例代码的注释行" // return os[m];"就OK了。
参考资料
https://my.oschina.net/u/1440018/blog/543245
https://segmentfault.com/a/1190000011403163
https://segmentfault.com/a/1190000018193387
Footer
------------------
虾皮一卷04.md
80 lines (58 sloc) 4.99 KB
3.cookies、sessionStorage和localStorage解释及区别?
cookie和session
cookie和session都是用来跟踪浏览器用户身份的会话方式。
区别:
1、保持状态:cookie保存在浏览器端,session保存在服务器端
cookie和session使用方式:
(1)cookie机制:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。
Cookie是服务器发给客户端的特殊信息,cookie是以文本的方式保存在客户端,每次请求时都带上它
(2)session机制:当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。如果用户禁用cookie,则要使用URL重写,可以通过response.encodeURL(url) 进行实现;API对encodeURL的结束为,当浏览器支持Cookie时,url不做任何处理;当浏览器不支持Cookie的时候,将会重写URL将SessionID拼接到访问地址后。
存储内容:
cookie只能保存字符串类型,以文本的方式;session通过类似与Hashtable的数据结构来保存,能支持任何类型的对象(session中可含有多个对象)
存储的大小:
cookie:单个cookie保存的数据不能超过4kb;session大小没有限制。
安全性:
cookie:针对cookie所存在的攻击:Cookie欺骗,Cookie截获;session的安全性大于cookie。
原因如下:
(1)sessionID存储在cookie中,若要攻破session首先要攻破cookie;
(2)sessionID是要有人登录,或者启动session_start才会有,所以攻破cookie也不一定能得到sessionID;
(3)第二次启动session_start后,前一次的sessionID就是失效了,session过期后,sessionID也随之失效。
(4)sessionID是加密的
(5)综上所述,攻击者必须在短时间内攻破加密的sessionID,这很难。
应用场景:
cookie:
(1)判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
(2)保存上次登录的时间等信息。
(3)保存上次查看的页面
(4)浏览计数
session:
(1)Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。
(2)网上商城中的购物车
(3)保存用户登录信息
(4)将某些数据放入session中,供同一用户的不同页面使用
(5)防止用户非法登录
缺点:
cookie:
(1)大小受限
(2)用户可以操作(禁用)cookie,使功能受限
(3)安全性较低
(4)有些状态不可能保存在客户端。
(5)每次访问都要传送cookie给服务器,浪费带宽。
(6)cookie数据有路径(path)的概念,可以限制cookie只属于某个路径下。
#### session:
(1)Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。
(2)依赖于cookie(sessionID保存在cookie),如果禁用cookie,则要使用URL重写,不安全
(3)创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度使用session变量将会导致代码不可读而且不好维护。
Web Storage
sessionStorage:
将数据保存在session对象中。所谓session,是指用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。session对象可以用来保存在这段时间内所要求保存的任何数据。
localStorage:
将数据保存在客户端本地的硬件设备(通常指硬盘,也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用。
这两者的区别在于,sessionStorage为临时保存,而localStorage为永久保存。
WebStorage的目的是克服由cookie所带来的一些限制,当数据需要被严格控制在客户端时,不需要持续的将数据发回服务器。
WebStorage两个主要目标:
(1)提供一种在cookie之外存储会话数据的路径。
(2)提供一种存储大量可以跨会话存在的数据的机制。
参考资料
https://blog.csdn.net/qq_41328247/article/details/108523868
-----------------
虾皮一卷05.md
82 lines (70 sloc) 2.92 KB
5.async await的理解
async+await是es7提出来的概念,它也是为了解决回调地狱的问题,它只是一种语法糖.
从本质上讲,await函数仍然是promise,其原理跟Promise相似.
不过比起Promise之后用then方法来执行相关异步操作,async/await则把异步操作变得更像传统函数操作。
async
async用于声明一个异步函数,该函数执行完之后返回一个 Promise 对象,可以使用 then 方法添加回调函数。请看下面代码:
async function helloAsync(){
return "helloAsync";
}
console.log(helloAsync()); // Promise {<resolved>: "helloAsync"}
helloAsync().then(v=>{
console.log(v); // helloAsync
})
通过上面的代码可以得出结论,async 函数内return的值会被封装成一个Promise对象,由于async函数返回Promise对象,所以该函数可以按照Promise对象标准使用then方法进行后续异步操作。
如果要把async函数方法跟Promise对象方法做对比的话,那么下面的Promise对象异步方法代码是完全相等于上面的async函数异步方法。
var helloAsync = function(){
return new Promise(function(resolve){
resolve("helloAsync");
})
}
console.log(helloAsync())
helloAsync().then(v=>{
console.log(v);
})
async函数运行的时候是同步运行的,Promise对象本身内容也是同步运行,这一点两者也是一致的,只有在then方法的时候才会被放入异步队列。
await
await 操作符用于等待一个 Promise 对象,它只能在异步函数 async function 内部使用。
async函数运行的时候是同步运行,但是当async函数内部存在await操作符的时候,则会把await操作符标示的内容同步执行,await操作符标示的内容之后的代码则被放入异步队列等待。
(await标识的代码表示该代码运行需要一定的时间,所以后续的代码得进异步队列等待)
下面放一段await标准用法:
function testAwait (x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function helloAsync() {
var x = await testAwait ("hello world");
console.log(x);
}
helloAsync ();
其实await多多少少对应了Promise对象异步方法里面的then方法,可以将上面代码改写成下面样式,结果也是一致的:
function testAwait (x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function helloAsync() {
var x = testAwait ("hello world");//此处x是一个Promise对象
x.then(function(value){
console.log(value);
});
}
helloAsync ();
// hello world
上述方法把await去掉,使用then取代,能够起到同样的作用。两者都是把特定区域的代码放到异步队列中执行。
参考资料
https://www.octgoodvps.com/archives/656
https://blog.csdn.net/weixin_41615439/article/details/88896675
------------------
虾皮一卷06.md
67 lines (50 sloc) 3.03 KB
原型链最终的指向
原型链最终的指向是Object的prototype, 而Object中的__proto__是null
如果原型指向改变了, 那么就应该在原型改变指向之后添加原型方法
function Person() {
}
Person.prototype.eat = function () {
console.log("吃东西");
};
var per = new Person();
console.dir(per);
console.dir(Person);
//实例对象中有__proto__原型
//构造函数中有prototype原型
//prototype是对象
//所以,prototype这个对象中也有__proto__,那么指向了哪里
//实例对象中的__proto__指向的是构造函数的prototype
//所以,prototype这个对象中__proto__指向的应该是某个构造函数的原型prototype
//Person的prototype中的__proto__的指向
//console.log(Person.prototype.__proto__);
//per实例对象的__proto__------->Person.prototype的__proto__---->Object.prototype的__proto__是null
console.log(per.__proto__ == Person.prototype); //true
console.log(per.__proto__.__proto__ == Person.prototype.__proto__); //true
console.log(Person.prototype.__proto__ == Object.prototype); //true
console.log(Object.prototype.__proto__); //null
为什么是null,而不是Object.prototype?
首先要明确一点,原型链是指对象的原型链,所以原型链上的所有节点都是对象,不能是字符串、数字、布尔值等原始类型。
另外,规范要求原型链必须是有限长度的(从任一节点出发,经过有限步骤后必须到达一个终点。显然也不能有环。)
那么,应该用什么对象作为终点呢?很显然应该用一个特殊的对象。
好吧,Object.prototype确实是个特殊对象,我们先假设用它做终点。那么考虑一下,当你取它的原型时应该怎么办?即
Object.prototype.__proto__;
应该返回什么?
取一个对象的属性时,可能发生三种情况:
如果属性存在,那么返回属性的值。
如果属性不存在,那么返回undefined。
不管属性存在还是不存在,有可能抛异常。
我们已经假设Object.prototype是终点了,所以看起来不能是情况1。
另外,抛出异常也不是好的设计,所以也不是情况3。
那么情况2呢,它不存在原型属性,返回undefined怎么样?也不好,因为返回undefined一种解释是原型不存在,但是也相当于原型就是undefined。
这样,在原型链上就会存在一个非对象的值。
所以,最佳选择就是null。一方面,你没法访问null的属性,所以起到了终止原型链的作用;
另一方面,null在某种意义上也是一种对象,即空对象,因为null一开始就是为表示一个“空”的对象存在的。这样一来,就不会违反“原型链上只能有对象”的约定。
所以,“原型链的终点是null”虽然不是必须不可的,但是却是最合理的。
参考资料
https://www.cnblogs.com/jane-panyiyun/p/12152721.html
https://www.imooc.com/wenda/detail/422208
----------------
虾皮一卷07.md
320 lines (231 sloc) 7.58 KB
谈谈你对作用域的理解?
涉及内容:
全局作用域
函数作用域
块级作用域
词法作用域
动态作用域 动态作用域跟 this 引用机制相关
全局作用域:
作用域,是指变量的生命周期(一个变量在哪些范围内保持一定值)。
全局变量:
生命周期将存在于整个程序之内。
能被程序中任何函数或者方法访问。
在 JavaScript 内默认是可以被修改的。
全局变量,虽然好用,但是是非常可怕的,这是所有程序员公认的事实。
显式声明:
带有关键字 var 的声明;
var testValue = 123;
var testFunc = function () {
console.log('just test')
};
/**---------全局变量会挂载到 window 对象上------------**/
console.log(window.testFunc) // ƒ () { console.log('just test') }
console.log(window.testValue) // 123
其实,我们写的函数如果不经过封装,也会是全局变量,他的生命周期也就是全局作用域;
隐式声明:
不带有声明关键字的变量,JS 会默认帮你声明一个全局变量!!!
function foo(value) {
result = value + 1; // 没有用 var 修饰
return result;
};
foo(123);// 124
console.log(window.result);
// 124 <= 挂在了 window全局对象上
现在,变量 result 被挂载到 window 对象上了!!!
函数作用域:
函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!!!
function bar() {
var testValue = 'inner';
}
console.log(testValue);
// 报错:ReferenceError: testValue is not defined
通过 return 访问函数内部变量:
function bar(value) {
var testValue = 'inner';
return testValue + value;
}
console.log(bar('fun'));
// "innerfun"
函数就像一个工厂,我们输入一些东西,它在内部加工,然后给我们一个加工产物;
通过 闭包 访问函数内部变量:
function bar(value) {
var testValue = 'inner';
var rusult = testValue + value;
function innser() {
return rusult;
};
return innser();
}
console.log(bar('fun'));
// "innerfun"
关于闭包,我不会在这篇文章过多描述,因为,想要描述闭包,本身需要跟本文章一样的长度;
立即执行函数:
这是个很实用的函数,很多库都用它分离全局作用域,形成一个单独的函数作用域;
(function() {
var testValue = 123;
var testFunc = function () {
console.log('just test');
};
})();
console.log(window.testValue);
// undefined
console.log(window.testFunc);
// undefined
它能够自动执行 (function() { //... })() 里面包裹的内容,能够很好地消除全局变量的影响;
块级作用域:
在 ES6 之前,是没有块级作用域的概念的。如果你有 C++ 或者 Java 经验,想必你对块级作用域并不陌生;
for(var i = 0; i < 5; i++) {
// ...
}
console.log(i) // 5
很明显,用 var 关键字声明的变量,在 for 循环之后仍然被保存这个作用域里;
这可以说明: for() { }仍然在,全局作用域里,并没有产生像函数作用域一样的封闭效果;
如果想要实现 块级作用域 那么我们需要用 let 关键字声明!!!
for(let i = 0; i < 5; i++) {
// ...
}
console.log(i)
// 报错:ReferenceError: i is not defined
在 for 循环执行完毕之后 i 变量就被释放了,它已经消失了!!!
同样能形成块级作用域的还有 const 关键字:
if (true) {
const a = 'inner';
}
console.log(a);
// 报错:ReferenceError: a is not defined
let 和 const 关键字,创建块级作用域的条件是必须有一个 { } 包裹:
{
let a = 'inner';
}
if (true) {
let b = 'inner';
}
var i = 0;
// ......
不要小看块级作用域,它能帮你做很多事情,举个栗子:
举一个面试中常见的例子:
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
// 5 5 5 5 5
}, 200);
};
这几乎是作用域的必考题目,你会觉得这种结果很奇怪,但是事实就是这么发生了;
这里的 i 是在全局作用域里面的,只存在 1 个值,等到回调函数执行时,用词法作用域捕获的 i 就只能是 5;
因为这个循环计算的 i 值在回调函数结束之前就已经执行到 5 了;我们应该如何让它恢复正常呢???
解法1:
调用函数,创建函数作用域
for(var i = 0; i < 5; i++) {
abc(i);
};
function abc(i) {
setTimeout(function() {
console.log(i);
// 0 1 2 3 4
}, 200);
}
这里相当于创建了5个函数作用域来保存,我们的 i 值;
解法2:
采用立即执行函数,创建函数作用域;
for(var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 200);
})(i);
};
原理同上,只不过换成了自动执行这个函数罢了,这里保存了 5 次 i 的值;
解法3:
let 创建块级作用域
for(let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 200);
};
词法作用域:
词法作用域是指一个变量的可见性,及其文本表述的模拟值(《JavaScript函数式编程》);
听起来,十分地晦涩,不过将代码拿来分析就非常浅显易懂了;
testValue = 'outer';
function afun() {
var testValue = 'middle';
console.log(testValue); // "middle"
function innerFun() {
var testValue = 'inner';
console.log(testValue); // "inner"
}
return innerFun();
}
afun();
console.log(testValue); // "outer"
当我们要使用声明的变量时:JS引擎总会从最近的一个域,向外层域查找;
再举一个一个实际的例子:
var testValue = 'outer';
function foo() {
console.log(testValue); // "outer"
}
function bar() {
var testValue = 'inner';
foo();
}
bar();
显然,当 JS 引擎查找这个变量时,发现全局的 testValue 离得更近一些,这恰好和 动态作用域 相反;
如上图所示,下面将讲述与 词法作用域相反的动态作用域;
动态作用域:
在编程中,最容易被低估和滥用的概念就是动态作用域(《JavaScript函数式编程》)。
在 JavaScript 中的仅存的应用动态作用域的地方:this 引用,所以这是一个大坑!!!!!
动态作用域,作用域是基于调用栈的,而不是代码中的作用域嵌套;
作用域嵌套,有词法作用域一样的特性,查找变量时,总是寻找最近的作用域;
同样是,词法作用域,例子2,同一份代码,如果 是动态作用域:
var testValue = 'outer';
function foo() {
console.log(testValue); // "inner"
}
function bar() {
var testValue = 'inner';
foo();
}
bar();
当然,JavaScript 除了this之外,其他,都是根据词法作用域查找!!!
为什么要理解动态作用域呢?因为,这能让你更好地学习 this 引用!!!
参考资料
https://juejin.cn/post/6844903584891420679
---------------
虾皮一卷08.md
66 lines (51 sloc) 3.31 KB
面试官:聊聊你对webpack相关的了解
webpack和grunt以及gulp有什么不同?
grunt和gulp是基于任务运行工具。 我们需要把我们要做的事,分配成各种各样的 任务,grunt和gulp它会自动执行指定的任务,像流水线,把资源放上去,通过不同的插件进行加工,它的插件非常丰富,能够帮我们打造各种的工作流。
webpack是模块打包器,webpack是所有的文件都当作模块来处理,也就是说webpack和grunt以及gulp 是完全不同的两类工具,
什么是bundle? 什么是chunk? 什么是module?
chunk: 代码块 一个chunk可能有很多的模块构造 用于代码的合并和分割
module:模块 在webpack世界中,一切都是模块 一个文件就是一个模块 webpack会从entry开始查找出项目所有的依赖的模块
bundle: webpack打包后生成的文件
什么是loader ? 什么是Plugin ? loader和plugin有什么区别?
webapck默认只能打包JS和JOSN模块,要打包其它模块,需要借助loader,loader就可以让模块中的内容转化成webpack或其它laoder可以识别的内容。
loader就是模块转换化,或叫加载器。不同的文件,需要不同的loader来处理。
plugin是插件,可以参与到整个webpack打包的流程中,不同的插件,在合适的时机,可以做不同的事件。
常见的loader有哪些? 你用的loader有哪些?它们有解决什么问题?
css-loader: 加载css模块 转换成模块化的css 让webpack识别它
style-loader: 把css代码注入到js中,通过DOM操作去加载CSS 把css代码放到了header标签中style标签中
babel-loader 把JS高阶转成JS低阶 pollyfill
eslint-loader: 检查JS代码是否符合某个规则
file-loader: 把文件输出到一个文件夹中,在代码中通过URL引用输出的文件
url-loader: 和file-loader类似,比file-loader强大一个,让小图片直接生成base64
less-loader: 把less代码转化css代码
html-loader: 处理html模块中的插件图片等
webpack中都有哪些插件,这些插件有什么作用?
html-webpack-plugin 自动创建一个HTML文件,并把打包好的JS插入到HTML文件中
clean-webpack-plugin 在每一次打包之前,删除整个输出文件夹下所有的内容
mini-css-extrcat-plugin 抽离CSS代码,放到一个单独的文件中
optimize-css-assets-plugin 压缩css
loader和plugin有什么区别?
loader 叫 加载器 或 转换器。
webpack中一切都是模块,但是webpack默认只能处理js和json模块,如果你想处理非JS模块,就需要借助loader。
让loader帮我们处理。Loader的作用是让webpack可以处理非JS模块 loader会把非JS模块中的内容转成新的内容。
plugin 插件 扩展webpack的功能,让webpack更加强大,在webpack构建的生命周期中,可以执行不同的插件,影响输出的结果。
什么是同源策略?
同源策略是为了保证用户信息安全,是浏览器的机制,防止恶意的网站窃取别人的数据,只允许访问来自同一个站点的资源
同源是指:
协议相同,域名相同,端口相同。这三者相同就是同源。如果不同源,浏览器就会作出限制。
参考资料
https://blog.csdn.net/weixin_46628546/article/details/108954255
-------------
虾皮一卷09.md
65 lines (43 sloc) 3.41 KB
面试官:说说http https的区别,https如何保证安全的?
http https的区别?
一、传输信息安全性不同
1、http协议:是超文本传输协议,信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。
2、https协议:是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全。
二、连接方式不同
1、http协议:http的连接很简单,是无状态的。
2、https协议:是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
三、端口不同
1、http协议:使用的端口是80。
2、https协议:使用的端口是443.
四、证书申请方式不同
1、http协议:免费申请。 2、https协议:需要到ca申请证书,一般免费证书很少,需要交费。传输信息安全性不同、连接方式不同、端口不同、证书申请方式不同
一、传输信息安全性不同
1、http协议:是超文本传输协议,信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。
2、https协议:是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全。
二、连接方式不同
1、http协议:http的连接很简单,是无状态的。
2、https协议:是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
三、端口不同
1、http协议:使用的端口是80。
2、https协议:使用的端口是443.
https做了什么保证它是安全的?
使用,SSL/TLS协议来保证https的安全。
SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。
(1)客户端向服务器端发起SSL连接请求;
(2) 服务器把公钥发送给客户端,并且服务器端保存着唯一的私钥
(3)客户端用公钥对双方通信的对称秘钥进行加密,并发送给服务器端
(4)服务器利用自己唯一的私钥对客户端发来的对称秘钥进行解密,
(5)进行数据传输,服务器和客户端双方用公有的相同的对称秘钥对数据进行加密解密,可以保证在数据收发过程中的安全,即是第三方获得数据包,也无法对其进行加密,解密和篡改。
如何保证公钥不被篡改?
将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。
公钥加密计算量太大,如何减少耗用的时间?
每一次对话(session),客户端和服务器端都生成一个”对话密钥”(session key),用它来加密信息。
由于”对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密”对话密钥”本身,这样就减少了加密运算的消耗时间。
(1) 客户端向服务器端索要并验证公钥。
(2) 双方协商生成”对话密钥”。
(3) 双方采用”对话密钥”进行加密通信。上面过程的前两步,又称为”握手阶段”(handshake)。
参考资料
https://zhuanlan.zhihu.com/p/354021419
https://segmentfault.com/a/1190000039683023
http://blog.itblood.com/1116.html
--------------------
虾皮一卷10.md
24 lines (14 sloc) 1.91 KB
面试官:说说http状态码,4开头和5开头具体有什么区别?
访问网站所反应的信号叫做HTTP状态码,是以三个数字为主的,我们看不到只是浏览器可以检测到这样的信号。
也就是用以表示网页服务器HTTP响应状态的3位数的数字代码,http状态码是由RFC2616规范定义的,并得到了RFC 2518、RFC 2817、RFC 2295、RFC 2774、RFC 4918等规范扩展。
http状态码的第一个数字代表了相应的一种状态,通常共有五种状态形式。
5开头的http状态码
代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。
除非这是一个HEAD 请求,否则服务器应当包含一个解释当前错误状态以及这个状况是临时的还是永久的解释信息实体。浏览器应当向用户展示任何在当前响应中被包含的实体。这些状态码适用于任何响应方法。
4开头的http状态码
代表了客户端看起来可能发生了错误,妨碍了服务器的处理。除非响应的是一个 HEAD 请求,否则服务器就应该返回一个解释当前错误状况的实体,以及这是临时的还是永久性的状况。这些状态码适用于任何请求方法。
浏览器应当向用户显示任何包含在此类错误响应中的实体内容。
如果错误发生时客户端正在传送数据,那么使用TCP的服务器实现应当仔细确保在关闭客户端与服务器之间的连接之前,客户端已经收到了包含错误信息的数据包。
如果客户端在收到错误信息后继续向服务器发送数据,服务器的TCP栈将向客户端发送一个重置数据包,以清除该客户端所有还未识别的输入缓冲,以免这些数据被服务器上的应用程序读取并干扰后者。
参考资料
http://www.xiaokaiseo.com/255.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通