1.函数柯里化
https://www.jianshu.com/p/2975c25e4d71
函数柯里化是一种闭包。
最后的面试题例子 ,如果打印的话就是执行隐式转换的toSTring console.log应该也算是一种赋值操作吧
vue源码的patch也用了函数柯里化,就是为了把执行在多个平台的patch函数分开,而不是在函数里面进行判断。
2、typeof在判断array类型的时候也会返回‘object’,只能用来检测5种基础类型。(我这个笨蛋,在看vue源码的时候忘记了这个 一直思考 为啥数组不给响应式。。)
3、那些UI框架中的折叠面板什么的为什么 不是在data中声明的数据就不能折叠和打开了呢?
因为结合vue的ui框架中都是用vue写的。而这个数据如果不是在data或者其他中声明 就是一个普通的对象(没有戈getter和setter函数)这样仅仅是一个对象 不依靠vue的setter和getter去更新dom 这样dom怎么会更新呢?这些框架内部想要更新dom肯定都是通过vue的虚拟dom这些一步一步去更新的。脑子短路了 想好久。。。。
4、发布/订阅模式和观察者模式的区别
观察者模式只有2个角色 发布者和订阅者, 他们是直接联系,通过notify去循环调用update。 vue双向绑定就是观察者模式(下文例子中的这个有个观察者列表,vue源码中把这个省略掉了 直接在dep中定义了个数组来代替观察者list,更简便)
发布订阅者模式 多了一个调度中心 由调度中心去操控
https://www.jianshu.com/p/9f2c8ae57cac
5、Vue 能做哪些性能优化?
①、路由懒加载
②、组件按需加载
③、keep-alive
④、preload/prefetch
⑤、key
⑥、减少不必要的响应式依赖
⑦、预加载和懒加载
⑧、v-if / v-show,computed / watch
⑨、事件销毁
⑩、按需 Polyfill、模板预编译
...
5、axios能取消请求吗?
可以 提供有这个函数。
比如在重复点击的时候会重复的去请求服务端的接口,这时候可以在请求前去清除上一次没有响应的相关的请求。
https://www.jianshu.com/p/22b49e6ad819
6、简单理解websoket协议?
websoket不同于http协议,http协议是单向的 只能客户端向服务端发送消息,而websoket是双向的 可以互发消息
需要依靠http建立连接。
简单来说,WebSocket是一种协议,与HTTP协议一样位于应用层,都是TCP/IP协议的子集。HTTP协议是单向通信协议,只有客户端发起HTTP请求,服务端才会返回数据。而WebSocket协议是双向通信协议,在建立连接之后,客户端和服务器都可以主动向对方发送或接受数据。WebSocket协议建立的前提需要借助HTTP协议,建立连接之后,持久连接的双向通信就与HTTP协议无关了。
使用:
// 打开WebSocket, 传递的参数url没有同源策略的限制。
let websocket = new WebSocket(url)
// 监听open事件,在成功建立websocket时向url发送纯文本字符串数据(如果是对象则必须序列化处理)。
websocket.onopen = () => {
if (websocket.readyState === WebSocket.OPEN) {
websocket.send('hello world')
}
}
// 监听message事件,在服务器响应时接受数据。返回的数据存储在事件对象中。
websocket.onmessage = e => {
let data = e.data
console.log(data)
}
// 监听error事件,在发生错误时触发,连接不能持续。
websocket.onerror = () => {
console.log('websocket connecting error!!')
}
// 监听close事件,在连接关闭时触发。只有close事件的事件对象拥有额外的信息。可以通过这些信息来查看关闭状态
websocket.onclose = e => {
let clean = e.wasClean // 是否已经关闭
let code = e.code // 服务器返回的数值状态码。
let reason = e.reason //服务器返回的消息。
参考连接:
https://www.cnblogs.com/unclekeith/p/8087182.html
7、node的超级简单入门学习
node有三大模块:
全局模块:何时何地都能直接访问,不需要引用
process.env 则是访问自己电脑下的环境变量
process.argv 则node *** a b c则是把a b c添加到一个有数据的一个数组里面,使用的时候可以做一些操作
系统模块:需要用require去引入,但是不需要下载
如:path 路径,最常见的则是path.resolve(__dirname,index.js) //则是返回index.js文件的绝对路径
fs 文件读写操作, ,fs.whriteFile(),fs.readFile()等
还有http模块 一会再说
自定义模块 就是我们平常自己写的一些代码 比如JS文件下的代码 函数 或者变量等等
exports->单个导出
module.exports={}->合并导出
require->在路径中去引入,如果没有路径 直接是一个字符串 则取node_module中去查找
http模块可以帮我们创建服务器。
是服务器 可以在浏览器上输入地址去访问
app.js
let http = require('http');
let url = require('url');
let fs = require("fs");
http.createServer((req,res)=>{
let {pathname, query} = url.parse(req.url, true);
console.log(pathname)
fs.readFile(`./${pathname}`, (err, data)=>{
if(err){
res.writeHead(404);
res.end('404');
}else{
res.end(data);
}
})
}).listen(8888)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8888" method="GET">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
上面则是在浏览器下输入http://localhost:8888就访问了我们定义的服务器,然后获得地址/去访问这个文件 ,发现没有的话 则会返回404
如果访问http://localhost:8888/index.html则会拿到index.html然后去返回给浏览器 这样我们浏览器就可以显示这个表单了。
这样就算是交互成功了
接下来做一个简单的登录注册
设计api
首先明白get和post传输。login使用get传输, reg注册使用post传输
get因为数据在链接少 所以比较少 <=32k post数据在身体上 所以传输的数据上限比较大 <=2G
而且post的数据由于比较多,所以可以分段传输
加下来做个例子,简单的登录注册,有登录成功 密码错误和用户不存在 注册有用户已经存在和注册成功
html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
用户名:<input type="text" name="username" id="username">
密码:<input type="password" name="password" id="password">
<button id="login">登录</button>
<button id="reg">注册</button>
<script src="./jquery.min.js"></script>
<script>
$('#login').click(function(){
$.ajax({
url:"/login",
data:{
username:$('#username').val(),
password:$('#password').val()
},
success(res){
res = JSON.parse(res)
alert(res.msg)
}
})
})
$('#reg').click(function(){
$.ajax({
url:"/reg",
method: "post",
dataType:'json',
data:{
username:$('#username').val(),
password:$('#password').val()
},
success(res){
alert(res.msg)
}
})
})
</script>
</body>
</html>
服务端代码:
let http = require("http");
let url = require("url");
let fs = require("fs");
let querystring = require("querystring");
let user = {
admin: 12345
}
http.createServer((req, res)=>{
let path, get, post;
if(req.method == 'GET'){
let {pathname, query} = url.parse(req.url, true);
path = pathname;
get = query;
complete();
}else if(req.method == 'POST'){
path = req.url;
let arr = [];
req.on('data', buffer=>{
arr.push(buffer);
})
req.on('end', ()=>{
post = querystring.parse(Buffer.concat(arr).toString());
complete();
})
}
function complete(){
if(path == '/login'){
res.writeHead(200,{
'content-Type': "text/plain;charset=utf-8"
})
let {username, password} = get;
if(!user[username]){
res.end(JSON.stringify({
err: 1,
msg:'用户名不存在'
}))
}else if(user[username] !=password){
res.end(JSON.stringify({
err: 1,
msg:'密码错误'
}))
}else{
res.end(JSON.stringify({
err: 0,
msg:'登录成功'
}))
}
}else if(path == '/reg'){
res.writeHead(200,{
'content-Type': "text/plain;charset=utf-8"
})
let {username, password} = post;
if(user[username]){
res.end(JSON.stringify({
err: 1,
msg:'用户存在'
}))
}else{
user[username] = password;
res.end(JSON.stringify({
err: 0,
msg:'注册成功'
}))
}
}else{
let path1 = './' + path;
fs.readFile(path1, (err,data)=>{
if(err){
res.end('404');
}else{
res.end(data);
}
})
}
}
}).listen(8080)
这样既可实现。
但是有个问题 我不知道为啥登录的时候传回来的数据是Json格式,但是注册的时候返回的却是对象格式,明明在传输之前转换成了json格式的 。。。 百思不得其解
8、手写简单async/await
async/await单看是generator的语法糖。
generator简单说就是函数声明之后 执行就会返回一个遍历器 然后执行一席next()从开头或者yieldield执行到下一个yield(感觉像是打了断点)
具体看廖雪峰的generator
async/await语法糖就是使用Generator函数+自动执行器来运作的。 我们可以参考以下例子
// 定义了一个promise,用来模拟异步请求,作用是传入参数++
function getNum(num){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num+1)
}, 1000)
})
}
//自动执行器,如果一个Generator函数没有执行完,则递归调用
function asyncFun(func){
var gen = func();
function next(data){
var result = gen.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}
// 所需要执行的Generator函数,内部的数据在执行完成一步的promise之后,再调用下一步
var func = function* (){
var f1 = yield getNum(1);
var f2 = yield getNum(f1);
console.log(f2) ;
};
asyncFun(func);
在执行的过程中,判断一个函数的promise是否完成,如果已经完成,将结果传入下一个函数,继续重复此步骤。
参考自:https://segmentfault.com/a/1190000020785563?utm_source=tag-newest#item-2-3
9、JavaScript的惰性函数
有这个需求就是返回第一次执行这个函数的时间。
普通的做法则是声明一个变量 赋值为第一次的时间,每次进来判断这个变量是否存在 存在即不是第一次 ,不获取时间 ,直接返回。
这样的缺点就是每次进函数都要判断
var t;
function foo() {
if (t) return t;
t = new Date()
return t;
}
所以这个时候为了解决这个问题,就有了惰性函数,简单的说就是满足条件之后 就把函数的索引指向一个新的函数。这样就不用每次都判断了: 即重写函数
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
10、JavaScript的组合函数
一个函数传入的参数即为另一个函数返回的值 这样多老几层, 则看起来不友好 不优雅
我们需要写一个函数,输入 'kevin',返回 'HELLO, KEVIN'。
var toUpperCase = function(x) { return x.toUpperCase(); };
var hello = function(x) { return 'HELLO, ' + x; };
var greet = function(x){
return hello(toUpperCase(x));
};
greet('kevin');
还好我们只有两个步骤,首先小写转大写,然后拼接字符串。如果有更多的操作,greet 函数里就需要更多的嵌套,类似于 fn3(fn2(fn1(fn0(x))))。
试想我们写个 compose 函数:
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
};
var greet = compose(hello, toUpperCase);
greet('kevin');
利用 compose 将两个函数组合成一个函数,让代码从右向左运行,而不是由内而外运行,可读性大大提升。这便是函数组合。
最后写一个 compose 函数支持传入多个函数呢?这样就变成了:
compose(d, c, b, a)
我们直接抄袭 underscore 的 compose 函数的实现:
function compose() {
var args = arguments;
var start = args.length - 1;
return function() {
var i = start;
var result = args[start].apply(this, arguments);
while (i--) result = args[i].call(this, result);
return result;
};
};
这样就可以支持多个函数了的使用了
使用方法:
function compose() {
var args = arguments;
var start = args.length - 1;
return function () {
var i = start;
var result = args[start].apply(this, arguments);
while (i--) result = args[i].call(this, result);
return result;
};
};
var toUpperCase = function (x) { return x.toUpperCase(); };
var hello = function (x) { return 'HELLO, ' + x; };
var happy = function (x) { return 'happy ' + x; };
let greet = compose(happy, hello, toUpperCase);
console.log(greet('xff')); //happy HELLO, XFF
11、JavaScript函数记忆
函数记忆是指将上次的计算结果缓存起来,当下次调用时,如果遇到相同的参数,就直接返回缓存中的数据。
function add(a, b) {
return a + b;
}
// 假设 memoize 可以实现函数记忆
var memoizedAdd = memoize(add);
memoizedAdd(1, 2) // 3
memoizedAdd(1, 2) // 相同的参数,第二次调用时,从缓存中取出数据,而非重新计算一次
实现这样一个 memoize 函数很简单,原理上只用把参数和对应的结果数据存到一个对象中,调用时,判断参数对应的数据是否存在,存在就返回对应的结果数据。
函数的实现:
function memoize(f) {
var cache = {};
return function(){
var key = arguments.length + Array.prototype.join.call(arguments, ",");
if (key in cache) {
return cache[key]
}
else {
return cache[key] = f.apply(this, arguments)
}
}
}
即可。但是参数为对象的时候,就会自动调用 toString 方法转换成 [Object object],再拼接字符串作为 key 值。我们写个 demo 验证一下这个问题:
var propValue = function(obj){
return obj.value
}
var memoizedAdd = memoize(propValue)
console.log(memoizedAdd({value: 1})) // 1
console.log(memoizedAdd({value: 2})) // 1
所以进行改良
var memoize = function(func, hasher) {
var memoize = function(key) {
var cache = memoize.cache;
var address = '' + (hasher ? hasher.apply(this, arguments) : key);
if (!cache[address]) {
cache[address] = func.apply(this, arguments);
}
return cache[address];
};
memoize.cache = {};
return memoize;
};
var memoizedAdd = memoize(add, function(){
var args = Array.prototype.slice.call(arguments)
return JSON.stringify(args)
})
console.log(memoizedAdd(1, 2, 3)) // 6
console.log(memoizedAdd(1, 2, 4)) // 7
如果使用 JSON.stringify,参数是对象的问题也可以得到解决,因为存储的是对象序列化后的字符串。
函数记忆只是一种编程技巧,本质上是牺牲算法的空间复杂度以换取更优的时间复杂度,在客户端 JavaScript 中代码的执行时间复杂度往往成为瓶颈,因此在大多数场景下,这种牺牲空间换取时间的做法以提升程序执行效率的做法是非常可取的。
斐波拉契数可用,这个例子是用来表明一种使用的场景,也就是如果需要大量重复的计算,或者大量计算又依赖于之前的结果,便可以考虑使用函数记忆。而这种场景,当你遇到的时候,你就会知道的。
大佬链接
12、es6的definePropty和proxy
使用 defineProperty 和 proxy 的区别,当使用 defineProperty,我们修改原来的 obj 对象就可以触发拦截,而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截。
大佬链接
13、vue官方api阅读:
①、vue API的 Vue.directive 指令绑定怎么用?
在vue框架中,我们开发者,时常使用vue自带的指令,比如v-on,v-for,v-modal等等。
同样的,vue提供给开发者一些钩子,用以帮助开发者自定义vue指令,用法和v-on等是一样的。
这个写得清楚
②、Vue.use ?
这个是针对vue插件的api
https://segmentfault.com/a/1190000012296163
③、Vue.compile:将一个模板字符串编译成 render 函数。只在完整版时可用。
var res = Vue.compile('<div><span>{{ msg }}</span></div>')
new Vue({
data: {
msg: 'hello'
},
render: res.render,
staticRenderFns: res.staticRenderFns
})
④、Vue.observable 可以将一个对象响应式
返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器,用于简单的场景
const state = Vue.observable({ count: 0 })
const Demo = {
render(h) {
return h('button', {
on: { click: () => { state.count++ }}
}, `count is: ${state.count}`)
}
}
⑤、Vue.version 提供字符串形式的 Vue 安装版本号。这对社区的插件和组件来说非常有用,你可以根据不同的版本号采取不同的策略。
⑥、propsData 创建实例时传递 props。主要作用是方便测试。只用于 new 创建的实例中。
var Comp = Vue.extend({
props: ['msg'],
template: '<div>{{ msg }}</div>'
})
var vm = new Comp({
propsData: {
msg: 'hello'
}
})
⑦、renderError
当 render 函数遭遇错误时,提供另外一种渲染输出。其错误将会作为第二个参数传递到 renderError。这个功能配合 hot-reload 非常实用。
⑧、provide/inject
在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。
用法见:provide/inject
⑨、listeners
这个可用于多层组件中传递信息 ,跟上面的provide/inject 差不多呢
⑩、vm.on emit的使用
https://www.cnblogs.com/kadima-zy/p/emit.html
$forceUpdate 强制重新渲染vue实例 只影响当前自己的组件以及子组件
v-html和v-text
https://www.jianshu.com/p/a69173cd33c0
v-cloak
CSS属性加上这个元素之后一直到编译结束才会显示
v-cloak
在解决rem布局的时候会先布局错乱再恢复正常的问题时可以用这个来解决。
v-pre 跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
v-once 只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
14、Vue.extend的使用?
Vue.extend是vue的全局api
对比普通场景的使用:
组件模板都是事先定义好的,如果我要从接口动态渲染组件怎么办?
有内容都是在 #app 下渲染,注册组件都是在当前位置渲染。如果我要实现一个类似于 window.alert() 提示组件要求像调用 JS 函数一样调用它,该怎么办?
大佬链接
可以做一个自定义的全局的弹框 然后自定义传入相应数据
export const confirm = function (text, title, onConfirm = () => {}) {
if (typeof title === "function") {
onConfirm = title;
title = undefined;
}
const ConfirmCtor = Vue.extend(Confirm);
const getInstance = () => {
if (!instanceCache) {
instanceCache = new ConfirmCtor({
propsData: {
text,
title,
onConfirm,
},
});
// 生成dom
instanceCache.$mount();
document.body.appendChild(instanceCache.$el);
} else {
// 更新属性
instanceCache.text = text;
instanceCache.title = title;
instanceCache.onConfirm = onConfirm;
}
return instanceCache;
};
const instance = getInstance();
// 确保更新的prop渲染到dom
// 确保动画效果
Vue.nextTick(() => {
instance.visible = true;
});
};
作者:晨曦时梦见兮
链接:https://juejin.im/post/5e7c08bde51d455c4c66ddad
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
使用的时候直接引入这个组件 然后使用函数式的调用即可
15、事件队列的理解?
先来一个经典面试题:
setTimeout(function () {
console.log(4)
}, 0);
new Promise(function (resolve) {
console.log(1)
for (var i = 0;i < 10000;i++) {
i == 9999 && resolve()
}
console.log(2)
}).then(function () {
console.log(5)
});
console.log(3);
为什么打印的正确顺序是1,2,3,5,4
而不是1,2,3,4,5
原因:有一个事件循环,但是任务队列可以有多个。整个script代码,放在了macrotask queue中,setTimeout也放入macrotask queue。但是,promise.then放到了另一个任务队列microtask queue中。这两个任务队列执行顺序如下,取1个macrotask queue中的task,执行之。然后把所有microtask queue顺序执行完,再取macrotask queue中的下一个任务。代码开始执行时,所有这些代码在macrotask
queue中,取出来执行之。后面遇到了setTimeout,又加入到macrotask queue中,然后,遇到了promise.then,放入到了另一个队列microtask queue。等整个execution context
stack执行完后,下一步该取的是microtask queue中的任务了。因此promise.then的回调比setTimeout先执行。
链接:来自大佬的链接
16: vue.nextTick 的实现和原理
大佬链接
17、hoc 高阶函数
说到这里,我们就要思考一下高阶组件到底是什么概念,其实说到底,高阶组件就是:
一个函数接受一个组件为参数,返回一个包装后的组件。
18、二、HTTP与HTTPS有什么区别?
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
19、移动端的边框0.5px 怎么设置?
使用伪元素
给伪元素设置高度1px 然后背景颜色渐变 或者给y方向缩小0.5
https://www.cnblogs.com/sese/p/7067961.html
20、vue2.x中怎么修改数组中的某一项的值?
不能使用arr[i] = xxx;
需要使用arr.splice(index, 1, newitem);
删除和添加和修改都可以用之后函数
21、找出字符串中出现次数最多的字符
我只会用最low的方法
排序 然后一个一个的找
网上看到
通过obj[key] = num 遇到就累加
obj两种方法:普通的和reduce的
let testStr = 'asdasddsfdsfadsfdghdadsdfdgdasd';
function getMax(str) {
let obj = {};
for(let i in str) {
if(obj[str[i]]) {
obj[str[i]]++;
}else{
obj[str[i]] = 1;
}
}
let keys = Object.keys(obj); // 获取对象中所有key的值返回数组
let values = Object.values(obj); // 获取所有value返回数组
let maxVal = Math.max(...values);// Math.max可以找出传入参数的最大值,如:Math.max(1,2);这里可使用es6中的解构,
也可以使用Math.max.apply(Math,values)可认为是apply(Math.max, arr)
然后,arr是一个参数列表,对于max方法,其参数是若干个数,即Math.max(a, b, c, d, ...)
console.log(keys[values.indexOf(maxVal)],maxVal);
}
getMax(testStr);
// obj值:{a: 5, s: 7, d: 12, f: 4, g: 2, h: 1, s: 7,}
// 很牛的reduce方法
var testStr = 'asdasddsfdsfadsfdghdadsdfdgdasd';
var testArray = testStr.split('');
var a = testArray.reduce(function(prev,next){
if(next in prev) {
prev[next]++;
}else {
prev[next] = 1;
}
return prev
},{})
console.log(a)
正则:
let stringMax = (str) => {
str = str.split('').sort().join('');
var s = str.match(/(\w+)(\1)/g);
if(s === null) {
return str[0];
}
s = s.map(e => e=e+e[0]);
var out = s.sort((a,b) =>b.length - a.length);
console.log(out[0][0],out[0].length);
};
stringMax(testStr)
22、把数字金额转成货币格式?
网友评论