跳槽是为了得到更高的薪酬,或者者为了寻觅更具发展空间的公司一种常见的行为。我也于今年完成了一次跳槽,自然也有很多的感悟,作成本文供大家参考。
本文主要分成两个部分,一部分是我自己整理的面试大纲,另外一部分是面试官考察的问题。
关于面试大纲,我认为每个人都是千差万别的。由于面试官都是对简历所写的项目经验进行深挖或者者对你所掌握的基本技能进行考察。
我在简历上写的技能点包含了HTML
/CSS
/JavaScript
/ES6
/HTTP协议
/Vue
/微信小程序
/Node
/Express
/MySQL
/面向对象
/设计模式
根据以上,整理出面试大纲
起因:行框的排列会受到中间空白(回车空格)等的影响,由于空格也属于字符,这些空白也会被应用样式,占据空间,所以会有间隔,把字符大小设为0或者者将<li>标签写在一排,就没有空格了。
word-wrap:break-word;
text-shadow: 5px 5px 5px #ff0000;
box-shadow: 10px 10px 5px #888888
border-radius: 50%;
border-image: url(border.png) 30 30 round
clear: both;
缺点:进行清理浮动;会增加很多无意义的空标签,有违结构与体现的分离,在后期维护中将是噩梦;overflow: hidden;
缺点:不能配合position
一起使用,超出的部分会被隐藏;overflow: auto;
缺点:超出部分会出现滚动条;<style>.clearfloat { *zoom: 1;}.clearfloat:after { content: ""; height: 0; display: block; clear: both; visibility: hidden;}</style><!-- *是为了兼容低版本IE7浏览器 zoom是ie属性,设置缩放比例 --><div class="box clearfloat"> <div class="left"> <div class="right"></div>
#test { position: absolute; width: 100px; height: 100px; background-color: green; <!-- 三行代码缺一不可 --> margin: 0 auto; left: 0; right: 0;}
<div id="test"></div>
#test { position: absolute; width: 100px; height: 100px; background-color: green; <!-- 三行代码缺一不可 --> margin: 0 auto; left: 0; right: 0;}
<div id="test"></div>
#triangle { width: 0; height: 0; border-top: 40px solid transparent; border-left: 40px solid transparent; border-right: 40px solid transparent; border-bottom: 40px solid greenyellow; }
<div id="triangle"><div>
display: none;
和 visibility: hidden;
的区别?display:none;
不显示对应的元素,在文档布局中不再分配空间(回流+重绘)visibility:hidden;
隐藏对应元素,在文档布局中仍保留原来的空间(重绘)position
和 float
值相互叠加会发生什么?position:absolute/fixed;
优先级最高,有他们在时,float
不起作用,display
值需要调整。
BFC规定了内部的Block Box如何布局。
定位方案:
满足下列条件之一即可触发BFC
浮动元素碰到包含它的边框或者者浮动元素的边框停留。因为浮动元素不在文档流中,所以文档流的块框体现得就像浮动框不存在一样。浮动元素会漂浮在文档流的块框上。
浮动带来的问题:
清理浮动的方式:
:after
和::after
的区别是什么?:
表示伪类,::
表示伪元素:after
在CSS2.1的规范中,表示伪元素,随着WEB的发展,在CSS3的规范中,伪元素的语法被修改成使用双冒号,成为::after
html, body { padding: 0; margin: 0; width: 100%; height: 100%; } #box { position: relative; height: 100%; width: 200px; background-color: beige; } #box .block-1 { width: 200px; height: 100px; background-color: rosybrown; } #box .block-2 { position: absolute; top: 100px; bottom: 0; width: 200px; background-color: red; }
<div id="box"> <div class="block-1"></div> <div class="block-2"></div> </div>
function Parent(name) { this.name = name;}Parent.prototype.sayName = function() { console.log('parent name:', this.name);}function Child(name) { this.name = name;}Child.prototype = new Parent('father');Child.prototype.constructor = Child;Child.prototype.sayName = function() { console.log('child name:', this.name);}var child = new Child('son');child.sayName(); // child name: son// 这种方法存在两个缺陷:// 1.子类型无法给超类型传递参数,在面向对象的继承中,我们总希望通过 var child = new Child('son', 'father'); 让子类去调用父类的构造器来完成继承。而不是通过像这样 new Parent('father') 去调用父类。// 2.Child.prototype.sayName 必需写在 Child.prototype = new Parent('father') 之后,不然就会被覆盖掉。
function Parent(name) { this.name = name;}Parent.prototype.sayName = function() { console.log('parent name:', this.name);}Parent.prototype.doSomthing = function() { console.log('parent do something!');}function Child(name, parentName) { Parent.call(this, parentName); this.name = name;}Child.prototype.sayName = function() { console.log('child name:', this.name);}var child = new Child('son');child.sayName(); // child name: sonchild.doSomthing(); // TypeError: child.doSomthing is not a function// 处理了原型链继承带来的问题// 但存在缺陷:每次创立一个 Child 实例对象时候都需要执行一遍 Parent 函数,无法复用少量公用函数。
function Parent(name) { this.name = name;}Parent.prototype.sayName = function() { console.log('parent name:', this.name);}Parent.prototype.doSomething = function() { console.log('parent do something!');}function Child(name, parentName) { Parent.call(this, parentName); // 第二次调用 this.name = name;}Child.prototype.sayName = function() { console.log('child name:', this.name);}Child.prototype = new Parent(); // 第一次调用Child.prototype.construtor = Child;var child = new Child('son');child.sayName(); child.doSomething(); // 第一次调用构造函数显然是没有必要的,由于第一次调用构造函数时候不需要函数内部的那些实例属性,这么写只是想取得其原型上的方法罢了// 下面的寄生组合式继承处理了这个问题
function Parent(name) { this.name = name;}Parent.prototype.sayName = function() { console.log('parent name:', this.name);}function Child(name, parentName) { Parent.call(this, parentName); this.name = name; }Child.prototype = Object.create(Parent.prototype); //修改Child.prototype.construtor = Child;Child.prototype.sayName = function() { console.log('child name:', this.name);}var parent = new Parent('father');parent.sayName(); // parent name: fathervar child = new Child('son', 'father');child.sayName(); // child name: son
// tips:/* Object.create(proto, [propertiesObject]) Object.create()方法创立一个新对象,使用现有的对象来提供新创立的对象的__proto__ proto 新创立对象的原型对象。 propertiesObject 可选。 假如没有指定为undefined,则是要增加到新创立对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性形容符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。 */
this
this
是在执行上下文创立时确定的一个在执行过程中不可更改的变量this
只在函数调用阶段确定,也就是执行上下文创立的阶段进行赋值,保存在变量对象中。执行上下文,就是JavaScript引擎在执行一段代码之前将代码内部会用到的少量
变量
、函数
、this
提前公告而后保存在变量对象中的过程。
// 本题的涉及考点主要还是运算符的优先级function Foo() { getName = function() { console.log(1) } return this}Foo.getName = function() { console.log(2)}Foo.prototype.getName = function() { console.log(3)}var getName = function() { console.log(4)}function getName() { console.log(5)}// 写出以下函数的执行结果Foo.getName() // 2getName() // 4Foo().getName() // 1 getName() // 1new Foo.getName() // 2// new (Foo.getName)()new Foo().getName() // 3// (new Foo()).getName()new new Foo().getName() // 3// new((new Foo()).getName)()
引用类型
和包装对象
的区别在于生存期
引用类型所创立的对象会一直存在于堆内存中,而基本包装对象只存在于一瞬间
var str = 'hello';str.number = 10; //假设我们想给字符串增加一个属性number ,后端会有如下步骤/* var str = new String('hello'); // 1 找到对应的包装对象类型,而后通过包装对象创立出一个和基本类型值相同的对象 str.number = 10; // 2 通过这个对象调用包装对象下的方法 但结果并没有被任何东西保存 str =null; // 3 这个对象又被销毁*/alert(str.number); //undefined 当执行到这一句的时候,由于基本类型原本没有属性,后端又会重新重复上面的步骤/* var str = new String('hello'); // 1 找到基本包装对象,而后又新开拓一个内存,创立一个值为hello对象 str.number = undefined // 2 由于包装对象下面没有number这个属性,所以又会重新增,由于没有值,所以值是未定 ;而后弹出结果 str =null; // 3 这个对象又被销毁*/
instanceof
运算符用于测试构造函数的prototype属性能否出现在对象的原型链中的任何位置(Instanceof 的使用规则是: A instanceof B
A沿着proto这条线来找,同时B沿着prototype这条线来找,假如两条线能找到同一个引用,即同一个对象,那么就返回true。假如找到终点还未重合,则返回false。)
// 1、函数提升优先级高于变量提升// 2、表达式可以修改提升后变量的值test() // 2var test = function() { console.log(1)}function test() { console.log(2)}test() // 1 alert(a);var a=1;alert(a); function a(){alert(2)};alert(a);var a=3;alert(a);function a(){alert(4)};alert(a);a();// function a() {alert (4)}; // 1// 1// 3// 3// 报错
// new共经历了四个阶段// 1、创立一个空对象var obj = new Object()// 2、设置原型链obj.__proto__ = Func.prototype// 3、让Func中的this指向obj,并执行Func的函数体var result = Func.call(obj)// 4、判断Func的返回值类型// 假如是值类型,返回obj。假如是引用类型,就返回这个引用类型的对象// (tip: 构造函数默认 return this,不用写)if(typeof result === 'object') { return result} eles { return obj}
MVVM分为Model、View、ViewModel三者
Model
代表数据模型,数据和业务逻辑都在Model层中定义;View
代表UI视图,负责数据的展现;ViewModel
负责监听 Model 中数据的改变并且控制视图的升级,解决客户交互操作;
Model 和 View 并无直接关联,而是通过 ViewModel 来进行联络的,Model 和 ViewModel 之间有着双向数据绑定的联络。因而当 Model 中的数据改变时会触发 View 层的刷新,View 中因为客户交互操作而改变的数据也会在 Model 中同步。
这种模式实现了 Model 和 View 的数据自动同步,因而开发者只要要专注对数据的维护操作就可,而不需要自己操作 dom
简单来说就是使用数据劫持
和发布订阅的设计模式
实现的
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>MVVM双向数据绑定</title></head><body> <script> class Vue { constructor(options) { this.options = options this.$data = options.data this.$el = document.querySelector(options.el) this._directives = {}; // 数据劫持 this.Observer(this.$data) // 解析指令 this.Compile(this.$el) } Observer(data) { for(let key in data) { this._directives[key] = [] let val = data[key] let _obj = this._directives[key] Object.defineProperty(this.$data, key, { get: function() { return val }, set: function(newVal) { if(val !== newVal) { val = newVal _obj.forEach((item) => { item.update() }) } } }) } } Compile(el) { let nodes = el.children for(let i=0; i<nodes.length; i++) { let node = nodes[i] if(node.children.length) { this.Compile(node) } if(node.hasAttribute('v-text')) { let attrValue = node.getAttribute("v-text"); this._directives[attrValue].push(new Watch(node, this, attrValue, "innerHTML")) } if(node.hasAttribute('v-model')) { let _this = this let attrValue = node.getAttribute("v-model"); this._directives[attrValue].push(new Watch(node, this, attrValue, "value")) node.addEventListener('input', (function(){ return function() { _this.$data[attrValue] = node.value } })()) } } } } class Watch { constructor(el, vm, exp, attr) { this.el = el this.vm = vm this.exp = exp this.attr = attr this.update() } update() { this.el[this.attr] = this.vm.$data[this.exp] } } </script> <div id="app"> <h1>数据双向绑定</h1> <div> <div v-text="myText"></div> <input type="text" v-model="myText"> </div> </div> <script> const app = new Vue({ el: '#app', data: { myText: '今晚吃鸡呐,大吉大利!' } }); </script></body></html>
watch: { 'obj.a': { handler (newName, oldName) { console.log('obj.a changed') } }}computed: { a1 () { return this.obj.a }}
数据已经增加,但是视图并没有刷新;
起因在于在Vue实例创立时,obj.b
并未公告,因而就没有被Vue转换为响应式的属性,自然就不会触发视图的升级,这时就需要使用Vue的全局api Vue.$set()
addObjB () { // this.obj.b = 'obj.b' this.$set(this.obj, 'b', 'obj.b') console.log(this.obj)}
可以去掘金找这类文章看一下,Vue组件的通讯方式达6种之多
.prevent
: 提交事件不再重载页面;.stop
: 阻止单击事件冒泡;.self
: 当事件发生在该元素本身而不是子元素的时候会触发;.capture
: 事件侦听,事件发生的时候会调用;
$route 是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数
$router 是“路由实例”对象包括了路由的跳转方法,钩子函数等
hash模式: 在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash尽管在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因而对于后台来说,即便没有做到对路由的全覆盖,也不会返回 404 错误。
history模式: history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前台的 URL 必需和实际向后台发起请求的 URL 一致,如 http://www.xxx.com/items/id。后台假如缺少对 /items/id 的路由解决,将返回 404 错误。Vue-Router 官网里如此形容:“不过这种模式要玩好,还需要后端配置支持……所以呢,你要在服务端添加一个覆盖所有情况的候选资源:假如 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或者避免重新渲染
在vue 2.1.0 版本之后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。
delete只是被删除的元素变成了 empty/undefined 其余的元素的键值还是不变;
Vue.delete直接删除了数组 改变了数组的键值。
将需要读取的状态集中放在store
中
改变状态的方式是提交mutations
,这是一个同步的事务
异步逻辑应该封装在actions
中
state Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和板块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations 定义的方法动态修改Vuex
的store
中的状态或者数据。
getters 相似vue
的计算属性,主要用来过滤少量数据。
action 可以了解为通过将mutations
里面处里数据的方法变成可异步的解决数据的方法,简单的说就是异步操作数据。view层通过store.dispath
来分发action
modules 项目特别复杂的时候,可以让每一个板块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理
优点:Vue的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件,核心是响应式系统。
缺点:不支持低版本浏览器,最低仅支持ie9;不利于SEO的优化,首页加载耗时相对偏长少量。
let
和var
的区别if (true) { // --- 死区开始 --- tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError let tmp; // --- 死区结束 --- console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123}
let
为JavaScript新添加了块级作用域var a = [];for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); };}a[6](); // 10// 变量i是var命令公告的,在全局范围内都有效,所以全局只有一个变量i,每一次循环,变量i的值都会发生改变var a = [];for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); };}a[6](); // 6// 变量i是let公告的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。
定义:从数组或者对象中提取值,对变量进行赋值
let [a, b, c] = [1, 2, 3];let [ , , third] = ["foo", "bar", "baz"];third // "baz"let [x, , y] = [1, 2, 3];x // 1y // 3let [head, ...tail] = [1, 2, 3, 4];head // 1tail // [2, 3, 4]let [x, y, ...z] = ['a'];x // "a"y // undefinedz // []let [x, y = 10] = [7]
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };foo // "aaa"bar // "bbb"let { baz } = { foo: 'aaa', bar: 'bbb' };baz // undefinedvar {x, y = 5} = {x: 1};// 假如变量名与属性名不一致,必需写成下面这样。// foo 是模式, baz 才是变量,特别要注意区分模式和变量let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
let obj = { p: [ 'Hello', { y: 'World' } ]};let { p: [x, { y }] } = obj;x // "Hello"y // "World"// p 是模式,不会被复制,若想被赋值,可以写成如下形式let { p, p: [x, { y }] } = obj;x // "Hello"y // "World"p // ["Hello", {y: "World"}]
this
指向在定义的时候继承自外层的第一个普通函数的this
,若外层没有普通函数,指向的是window
this
指向arguments
会报未公告的错误。argumens
继承于该普通函数new
调用箭头函数会报错,由于箭头函数没有constructor
Promise的特点
状态一旦改变就再也不会发生改变了
Promise的缺点
const promise = new Promise(function(resolve, reject) { if (/* 异步操作成功 */){ resolve(value); } else { reject(error); }});
// 1. 假如传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。function fn(resolve){ setTimeout(function(){ resolve(123); },3000);}let p0 = new Promise(fn);let p1 = Promise.resolve(p0);// 返回为true,返回的 Promise 即是 入参的 Promise 对象。console.log(p0 === p1);//2. 假如这个值是个 thenable(thenable对象指的是具备then方法的对象),返回的 Promise 对象会“跟随”这个 thenable 的对象,采用它的最终状态(指 resolved/rejected/pending/settled)let promise = Promise.resolve($.ajax('/test/test.json'));// => promise对象promise.then(function(value){ console.log(value);});
// 类方法,多个 Promise 任务同时执行,返回最先执行结束的 Promise// 任务的结果,不论这个 Promise 结果是成功还是失败。 。
// 类方法,多个 Promise 任务同时执行。// 假如一律成功执行,则以数组的方式返回所有 Promise 任务的执行结果。// 假如有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。const promises = [2, 3, 5, 7, 11, 13].map(function (id) { return getJSON('/post/' + id + ".json");});Promise.all(promises).then(function (posts) { // ...}).catch(function(reason){ // ...});
Promise
对象最后状态如何,都会执行的操作promise.then(result => {···}).catch(error => {···}).finally(() => { // finally方法的回调函数不接受任何参数,这意味着没有办法知道, //前面的 Promise 状态究竟是fulfilled还是rejected。这表明, // finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。});
关于Promise
,不同面试官考察的深度因人而异,反正做好手写实现Promise
的准备就可
webview
和逻辑层appService
两个部分;webview
用来展示UI,appService
有来解决业务逻辑、数据及接口调用;JSBridge
实现通信,实现UI的渲染、事件的解决等。由于微信小程序的渲染线程和脚本线程是分开的,脚本线程独立在 JSCore
中,没有一个完整的浏览器对象。
WXS(weixin script),结合HTML,构建页面结构。
<wxs module="m1">var msg = "hello world"; module.exports.message = msg;</wxs><view>{{m1.message}}</view>
频繁客户交互的效果在小程序上体现是比较卡慢的, 例如页面有 2 个元素 A 和 B,客户在 A 上做 touchmove 手势,要求 B 也跟随移动。一次 touchmove
事件的响应过程为:
a、touchmove 事件从视图层(Webview)抛到逻辑层(App Service)
b、逻辑层(App Service)解决 touchmove 事件,再通过 setData 来改变 B 的位置
一次 touchmove 的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染,通信(JSBridge)的耗时比较大。此外 setData 渲染也会阻塞其它脚本执行,导致了整个客户交互的动画过程会有推迟。
onLoad() 页面加载时触发,只会调用一次,可获取当前页面路径中的参数;
onShow() 页面显示/切入前端时触发,一般用来发送数据请求(或者者 set to foreground);
onReady() 页面首次渲染完成时触发 只会调用一次,代表页面已可和视图层进行交互(当逻辑层通知渲染层 Send Initial Data,且渲染层 First Render 之后调用);
onHide() 页面隐藏/切入后端时触发(set to background), 如底部tab切换到其余页面或者小程序切入后端等;
onUnload() 页面卸载时触发(destroy),如redirectTo或者navigateBack到其余页面时。
GET /images/logo.gif HTTP/1.1
往返时延,在计算机网络中它也是一个重要的性能指标,它表示从发送端发送数据开始,到发送端收到来自接收端确实认(接收端收到数据后便立即发送确认),总共经历的时延;
只是数据报文的搬运工,不会对报文进行拆分和拼接操作,不保证有序且不丢失的传递到对端
在直播行业和即时对战游戏等实时性要求高的行业,UDP协议使用广泛
TCP 基本是和 UDP 反着来,建立连接断开连接都需要先需要进行握手。在传输数据的过程中,通过各种算法保证数据的可靠性,当然带来的问题就是相比 UDP 来说不那么的高效。是全双工协议。
用户端 ------ SYN = 1, ACK = 0, seq = x ------- > 服务端
用户端 < ----- SYN = 1, ACK = 1, seq = y, ack = x + 1 -------- 服务端
用户端 ------ ACK = 1, seq = x + 1, ack = y + 1------- > 服务端
用户端 ------ FIN ------- > 服务端
用户端 < ----- ACK -------- 服务端
用户端 < ----- FIN -------- 服务端
用户端 ------ ACK ------- > 服务端
ARQ协议其实就是超时重传机制。通过确认和超时重传机制保证数据的正确送达。
发送端窗口包含已发送但未收到应答的数据
和待发送的数据
发送端窗口是由接收窗口剩余大小决定的。接收方会把当前接收窗口的剩余大小写入应答报文,发送端收到应答后根据该值和当前网络拥塞情况设置发送窗口的大小,所以发送窗口的大小是不断变化的。
滑动窗口处理了数据的丢包、顺序不对和流量控制问题
继承
封装
和多态
明天需要上班,后续跟进补充!
网上体育商城系统源码 网站/电商SSH jsp css Javascri-pt框架
2022多语言海外矿机区块链源码/VUE开发/英文版矿机/原生app体验
九城手游源码 PHP理财游戏 复利 分红 拆分运营养成类
WordPress程序 RiPro日主题V6.6官方版美化包-子主题-child简洁大气集成后台美化包v1.6持续更新
最新坦白说QQ号解密在线生成源码,包后续升级
仿萝卜视频/影视APP/新UI影视APP/原生源码安卓影视app/可定制影视APP/安卓影视app
织梦 蓝色大气 通用企业PHP源码 dedecms5.7 公司官网 网站模板
广告公司网站源码传媒公司视频网站源码设计工作室dede红黑色模板
卖爆农产品商城微店手机模板
环保油漆企业通使用dedecms模板