问题:说一下 css 的盒模型css
盒模型分为标准模型和怪异盒模型(IE 盒模型)html
标准盒模型:盒模型的宽高只是内容(content)的宽高前端
怪异盒模型:盒模型的宽高是内容(content)+填充(padding)+边框(border)的总宽高vue
问题:css 如何设置两种模型react
/* 标准模型 */ box-sizing:content-box; /*IE模型*/ box-sizing:border-box;
问题:有没有遇到过边距重叠,怎么解决 边距重叠问题以下图所示linux
利用 BFC 去解决,下方详细说明 BFCgit
参考代码:利用 BFC 的特性,把另外一个盒子放入另外一个独立的 BFC 中,从而使得两个盒子之间互相不受影响es6
<section class="top"> <h1>上</h1> margin-bottom:30px; </section> <div style="overflow: hidden;"> <section class="bottom"> <h1>下</h1> margin-top:50px; </section> </div>
问题:说一下 BFCgithub
什么是 BFCweb
BFC(Block Formatting Context)格式化上下文,是 Web 页面中盒模型布局的 CSS 渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。
造成 BFC 的条件
BFC 的特性
rem 布局的本质是等比缩放,通常是基于宽度,假设将屏幕宽度分为 100 份,每份宽度是 1rem,1rem 的宽度是屏幕宽度/100,,而后子元素设置 rem 单位的属性
经过改变 html 元素的字体大小,就能够设置子元素的实际大小。
比 rem 更好的方案(缺点兼容很差)
vw(1vw 是视口宽度的 1%,100vw 就是视口宽度),vh(100vh 就是视口高度)
(两侧定宽,中间自适应)
这里写出五种实现方式:
/* css */ .box { display: flex; justify-content: center; height: 200px; } .left { width: 200px; background-color: red; height: 100%; } .content { background-color: yellow; flex: 1; } .right { width: 200px; background-color: green; } /* html */ <div class="box"> <div class="left"></div> <div class="content"></div> <div class="right"></div> </div>
/* css */ .box { height: 200px; } .left { width: 200px; background-color: red; float: left; height: 100%; } .content { background-color: yellow; height: 100%; } .right { width: 200px; background-color: green; float: right; height: 100%; } /* html */ <div class="box"> <div class="left"></div> <div class="right"></div> <div class="content"></div> </div>
/* css */ .box { position: relative; height: 200px; } .left { width: 200px; background-color: red; left: 0; height: 100%; position: absolute; } .content { background-color: yellow; left: 200px; right: 200px; height: 100%; position: absolute; } .right { width: 200px; background-color: green; right: 0; height: 100%; position: absolute; } /* html */ <div class="box"> <div class="left"></div> <div class="content"></div> <div class="right"></div> </div>
/* css */ .box { display: table; height: 200px; } .left { width: 200px; background-color: red; height: 100%; display: table-cell; } .content { background-color: yellow; height: 100%; display: table-cell; } .right { width: 200px; background-color: green; height: 100%; display: table-cell; } /* html */ <div class="box"> <div class="left"></div> <div class="content"></div> <div class="right"></div> </div>
/* css */ .box { display: grid; grid-template-columns: 200px auto 200px; grid-template-rows: 200px; } .left { background-color: red; } .content { background-color: yellow; } .right { background-color: green; } /* html */ <div class="box"> <div class="left"></div> <div class="content"></div> <div class="right"></div> </div>
这里写出五种实现方式:
/* css */ #box{ width: 400px; height: 200px; position: relative; background: red; } #box1{ width: 200px; height: 100px; position: absolute; top: 50%; left: 50%; margin-left: -100px; margin-top: -50px; background: green; } /* html */ <div id="box"> <div id="box1"> </div> </div>
/* css */ #box{ width: 800px; height: 400px; position: relative; background: red; } #box1{ width: 100px; height: 50px; position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; background: green; } /* html */ <div id="box"> <div id="box1"> </div> </div>
/* css */ #box{ width: 400px; height: 200px; background: #f99; display: flex; justify-content: center;//实现水平居中 align-items: center;//实现垂直居中 } #box1{ width: 200px; height: 100px; background: green; } /* html */ <div id="box"> <div id="box1"> </div> </div>
/* css */ #box{ width: 400px; height: 200px; background: red; position: relative; } #box1{ width: 200px; height: 100px; background: #9ff; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } /* html */ <div id="box"> <div id="box1"> </div> </div>
/* css */ #box{ display: table-cell; vertical-align: middle } #box1{ margin: 0 auto; } /* html */ <div id="box"> <div id="box1"> </div> </div>
可以读取其余函数内部变量的函数,或简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。
闭包的用途:
闭包是 JavaScript 一个很是重要的特性,这意味着当前做用域老是可以访问外部做用域中的变量。 由于 函数 是 JavaScript 中惟一拥有自身做用域的结构,所以闭包的建立依赖于函数。
类似之处:
手动实现 call 方法:
Function.prototype.myCall = function(context = window, ...rest) { context.fn = this; //此处this是指调用myCall的function let result = context.fn(...rest); //将this指向销毁 delete context.fn; return result; };
手动实现 apply 方法:
Function.prototype.myCall = function(context = window, params = []) { context.fn = this; //此处this是指调用myCall的function let result if (params.length) { result = context.fn(...params) }else { result = context.fn() } //将this指向销毁 delete context.fn; return result; };
手动实现 bind 方法:
Function.prototype.myBind = function(oThis, ...rest) { let _this = this; let F = function() {} // 根据 bind 规定,若是使用 new 运算符构造 bind 的返回函数时,第一个参数绑定的 this 失效 let resFn = function(...parmas) { return _this.apply(this instanceof resFn ? this : oThis, [ ...rest, ...parmas ]); }; // 继承原型 if (this.prototype) { F.prototype = this.prototype; resFn.prototype = new F; } return resFn; };
每一个函数都有 prototype
每个对象都有 __proto__
实例的 __proto__
指向构造函数的 prototype
js 引擎会沿着 __proto__
-> ptototype 的顺序一直往上方查找,找到 Object.prototype 为止,Object 为原生底层对象,到这里就中止了查找,若是没有找到,就会报错或者返回 undefined
经常使用继承:组合继承,寄生组合继承
组合继承: 利用 call 继承父类上的属性,用子类的原型等于父类实例去继承父类的方法
缺点:调用两次父类,形成性能浪费
function Parent(name) { this.name = name; } Parent.prototype.say = function() { console.log(this.name); }; function Child(name) { Parent.call(this, name) } Child.prototype = new Parent; let c = new Child("YaoChangTuiQueDuan"); c.say()
寄生组合继承:利用 call 继承父类上的属性,用一个干净的函数的原型去等于父类原型,再用子类的原型等于干净函数的实例
function Parent(name) { this.name = name; } Parent.prototype.say = function() { console.log(this.name); }; function ExtendMiddle() {} function Child(name) { Parent.call(this, name) } ExtendMiddle.prototype = Parent.prototype; Child.prototype = new ExtendMiddle let c = new Child("YaoChangTuiQueDuan"); c.say()
请参考文章 Eventloop 不可怕,可怕的是赶上 Promise
function create(...rest) { // 建立一个空的对象 let obj = new Object() // 得到构造函数 let Con = rest.shift() // 连接到原型 obj.__proto__ = Con.prototype // 绑定 this,执行构造函数 let result = Con.apply(obj, arguments) // 确保 new 出来的是个对象 return typeof result === 'object' ? result : obj }
Promise 的几个特性:
Promise 的简单实现:
class MyPromise { constructor(fn) { this.resolvedCallbacks = []; this.rejectedCallbacks = []; this.state = "PADDING"; this.value = ""; fn(this.resolve.bind(this), this.reject.bind(this)); } resolve(value) { if (this.state === "PADDING") { this.state = "RESOLVED"; this.value = value; this.resolvedCallbacks.forEach(cb => cb()); } } reject(value) { if (this.state === "PADDING") { this.state = "REJECTED"; this.value = value; this.rejectedCallbacks.forEach(cb => cb()); } } then(resolve = function() {}, reject = function() {}) { if (this.state === "PADDING") { this.resolvedCallbacks.push(resolve); this.rejectedCallbacks.push(reject); } if (this.state === "RESOLVED") { resolve(this.value); } if (this.state === "REJECTED") { reject(this.value); } } }
能够在 Object 的 prototype 上挂载一个 Symbol.iterator
方法,该方法返回一个对象,对象中包含 next
方法, next
方法也会返回一个对象,对象中包含 value
和 done
。 value 表示 每次迭代完成的值,done 表示是否迭代完成,为 false 时会继续迭代,为 true 表示迭代完成,中止迭代
Object.prototype[Symbol.iterator] = function () { let values = Object.values(this); let keys = Object.keys(this); let len = 0; return { next() { return { value: { value: values[len], key: keys[len++] }, done: len > values.length } } } }
为了方便,Node为每一个模块提供一个 exports 变量,指向 module.exports,至关于 exports 是 module.exports 地址的引用
会产生的问题:若是将 exports 新赋值了一个对象,如: exports = {},这个时候,就会打断与 module.exports 的联系,会致使导出不成功
如何区分深拷贝与浅拷贝,简单点来讲,就是假设B复制了A,当修改A时,看B是否会发生变化,若是B也跟着变了,说明这是浅拷贝,若是B没变,那就是深拷贝。
因为 js 中分为两种变量类型,基本数据类型和引用数据类型,基本数据类型包括,number,string,boolean,null,undefined,symbol。
引用数据类型(Object类)有常规名值对的无序对象 {a:1}
,数组 [1,2,3]
,以及函数等。
而这两类数据存储分别是这样的:
基本类型--名值存储在栈内存中,例如let a=1;
当你 b=a 复制时,栈内存会新开辟一个内存,例如这样:
因此当你此时修改 a=2,对 b 并不会形成影响,由于此时的 b 已更换内存地址,不受 a 的影响了
引用数据类型--名存在栈内存中,值存在于堆内存中,可是栈内存会提供一个引用的地址指向堆内存中的值
当 b=a 进行拷贝时,其实复制的是 a 的引用地址,而并不是堆里面的值。
而当咱们 a[0]=1 时进行数组修改时,因为 a 与 b 指向的是同一个地址,因此天然 b 也受了影响,这就是所谓的浅拷贝了。
那,若是咱们须要实现深拷贝的效果,就须要在堆内存中也开辟一个新的的内存空间来存放 b 的值,如图:
深拷贝的实现方式:
json.parse & json.stringify
先将一个对象转为json对象。而后再解析这个json对象,但此方法有几个缺点:
递归方式实现深拷贝(ps:这里只是简易版,完整版建议看 lodash 的 cloneDeep 方法源码)
function deepClone(obj) { let objClone = Array.isArray(obj) ? [] : {}; let toString = Object.prototype.toString; for (key in obj) { if (obj.hasOwnProperty(key)) { //判断ojb子元素是否为对象,若是是,递归复制 if ( toString.call(obj[key]) === "[object Object]" || toString.call(obj[key]) === "[object Array]" ) { objClone[key] = deepClone(obj[key]); } else { //若是不是,简单复制 objClone[key] = obj[key]; } } } return objClone; }
栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间。 堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型
为何会有栈内存和堆内存之分?
一般与垃圾回收机制有关。为了使程序运行时占用的内存最小。
当一个方法执行时,每一个方法都会创建本身的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将天然销毁了。所以,全部在方法中定义的变量都是放在栈内存中的;
当咱们在程序中建立一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(由于对象的建立成本一般较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即便方法结束后,这个对象还可能被另外一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。
方案一:
分页,懒加载,把数据分页,而后每次接受必定的数据,避免一次性接收太多
方案二:
setInterval,setTimeout,requestAnimationFrame 分批渲染,让数据在不一样帧内去作渲染
方案三:
使用 virtual-scroll,虚拟滚动。
这种方式是指根据容器元素的高度以及列表项元素的高度来显示长列表数据中的某一个部分,而不是去完整地渲染长列表,以提升无限滚动的性能
virtual-scroll原理:
在用户滚动时,改变列表在可视区域的渲染部分
以下图所示:
startOffset 和 endOffset 会撑开容器元素的内容高度,让其可持续的滚动;此外,还能保持滚动条处于一个正确的位置。
当浏览器再次访问一个已经访问过的资源时,它会这样作:
浏览器缓存的位置:
缓存的实现: 强缓存和协商缓存都是根据 HTTP Header 来实现的
回流:布局或者几何属性须要改变就称为回流。 重绘:当节点须要更改外观而不会影响布局的,好比改变 color 就叫称为重绘
区别:
回流必将引发重绘,而重绘不必定会引发回流。好比:只有颜色改变的时候就只会发生重绘而不会引发回流 当页面布局和几何属性改变时就须要回流
好比:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变
MVVM是双向数据绑定
view 层经过事件去绑定 Model 层, Model 层经过数据去绑定 View 层
在以前,渲染数据时,会直接替换掉 DOM 里的全部元素,换成新的数据,为了渲染无用 DOM 所形成的性能浪费,因此出现了 Virtual DOM, Virtual DOM 是虚拟 DOM,是用 js 对象表示的树结构,把 DOM 中的属性映射到 js 的对象属性中,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等。当数据改变时,从新渲染这个 js 的对象结构,找出真正须要更新的 DOM 节点,再去渲染真实 DOM。Virtual DOM 本质就是在 JS 和 DOM 之间作了一个缓存
由于 DOM 是属于渲染引擎中的东西,而 JS 又是 JS 引擎中的东西。当咱们经过 JS 操做 DOM 的时候,其实这个操做涉及到了两个线程之间的通讯,那么势必会带来一些性能上的损耗。操做 DOM 次数一多,也就等同于一直在进行线程之间的通讯,而且操做 DOM 可能还会带来重绘回流的状况,因此也就致使了性能上的问题。
Vue 内部使用了 Object.defineProperty()
来实现数据响应式,经过这个函数能够监听到 set
和 get
的事件。
Object.defineProperty()
给 data 中的属性去设置 set
, get
事件get
事件中去收集观察者依赖set
事件中去通知每个观察者,作到所有更新vuex 就像一个全局的仓库,公共的状态或者复杂组件交互的状态咱们会抽离出来放进里面。
vuex的核心主要包括如下几个部分:
commit mutations
去修改状态使用:
父子组件:父组件经过 Props 传递子组件,子组件经过 $emit 通知父组件 兄弟组件:可使用 vuex 全局共享状态,或 eventBus
能够经过插件 vuex-persistedstate 来解决 插件原理:利用 HTML5 的本地存储 + Vuex.Store 的方法,去同步本地和 store 中的数据,作到同步更新
待补充...
两种路由模式分为 hash 模式 和 history 模式
hash 模式:
hash 模式背后的原理是 onhashchange 事件,能够在window对象上监听这个事件,在 hash 变化时,浏览器会记录历史,而且触发事件回调,在 hash 模式中,地址栏中会带一个 #
history 模式:
前面的 hashchange,你只能改变 # 后面的 url 片断,而 history api 则给了前端彻底的自由
history api能够分为两大部分,切换和修改,参考 MDN
使用 history 须要服务端的支持,在hash模式下,前端路由修改的是#中的信息,而浏览器请求时是不带着的,因此没有问题.可是在history下,你能够自由的修改path,当刷新时,若是服务器中没有相应的响应或者资源,会刷出一个404来
待补充...
待补充...
待补充...
待补充...
待补充...
待补充...
已知以下数组: var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10]; 编写一个程序将数组扁平化去并除其中重复部分数据,最终获得一个升序且不重复的数组 方法一: function handleArr(arr) { let flatten = (arr) => arr.push ? arr.reduce((pre, current) => [...pre, ...flatten(current)], []) : [arr]; return [...new Set(flatten(arr))].sort((a, b) => a - b); } 方法二: [...new Set(arr.toString().split(",").map(Number))].sort((a,b)=> a - b) 方法三: [...new Set(arr.flat(Infinity))].sort((a,b)=>{ return a - b})
let test = (function (a) { this.a = a; return function (b) { console.log(this.a + b); } })((function(a){ return a; })(1,2)) test(4)
答案:5
解析:咱们把(function (a) { this.a = a; return function (b) { console.log(this.a + b); } })()
自执行函数体叫作 z1
把 (function(a){ return a; })(1,2)
叫作 z2
把function (b) { console.log(this.a + b); }
叫作 n1
test 函数为匿名自执行函数z1
的返回值,实际上 test 函数为 n1
,函数 z1
接收 a 做为参数,这里的 a 实际上为自执行函数 z2
返回值为 1, 那么 n1
函数中的参数 a 就是 1,在函数 z1
中 this.a = a
这里的 this 指向的是 window
因此在 window
上挂载了一个 a 的属性,值为 1
在 text(4) 执行时,传入了 b 为 4, 函数 n1
中的 this 指向的是 window
,window 中有以前挂载的属性 a 为 1 因此函数执行结果为 5
做者:腰长腿却短连接:https://juejin.im/post/5ca1ac256fb9a05e6938d2d1来源:掘金著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。