CSS
1、Flex布局
Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。
采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
使用Flex时,比使用inline-block和float时重排更快,在布局时优先考虑Flex。
2、css清除浮动
浮动:浮动使元素脱离文档流,可设置文字环绕,但会导致父元素的高度塌陷。
clear属性
- 追加元素并设置clear属性
clear: both; 使用CSS样式插入伪元素
.clearfix::after { content: ""; display: block; clear: both; }
创建父级BFC
- 父元素设置overflow: hidden;
- 父元素浮动(float)
- 父元素display: inline-block;
给父元素设置高度
3、选择器优先级
!important > 行内样式 > #id > .class > tag > * > 继承 > 默认
CSS选择器从右往左
解析,推荐使用BEM
命名
4、BFC
BFC块级格式化上下文 (Block Formatting Context) ,是一个独立的渲染区域,让处于 BFC 内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。
触发条件
- 根元素或其它包含它的元素
- 浮动元素 (元素的 float 不是 none)
- 绝对定位元素 (元素具有 position 为 absolute 或 fixed)
- 内联块 (元素具有 display: inline-block)
- 表格单元格 (元素具有 display: table-cell,HTML表格单元格默认属性)
- 表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)
- 具有overflow 且值不是 visible 的块元素
- 弹性盒(flex或inline-flex)
- display: flow-root
column-span: all
规则
- 属于同一个 BFC 的两个相邻 Box 垂直排列
- 属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠
- BFC 中子元素的 margin box 的左边, 与包含块 (BFC) border box的左边相接触 (子元素 absolute 除外)
- BFC 的区域不会与 float 的元素区域重叠
- 计算 BFC 的高度时,浮动子元素也参与计算
- 文字层不会被浮动层覆盖,环绕于周围
应用
- 阻止margin重叠
- 可以包含浮动元素 —— 清除内部浮动(清除浮动的原理是两个div都位于同一个 BFC 区域之中)
- 自适应两栏布局
可以阻止元素被浮动元素覆盖
5、盒模型
标准盒模型(content-box)和IE盒模型(border-box)。
标准盒模型
浏览器默认模型,即box-sizing: content-box;
width = content-width
height = content-heightIE盒模型
box-sizing: border-box;
width = content-width + padding-width + border-width
height = content-height + padding-height + border-heightoffsetWidth
返回元素的宽度(包括元素宽度、内边距和边框,不包括外边距)
offsetWidth = content-width + padding-width + border-widthclientWidth
元素的内部宽度(包括元素宽度、内边距,不包括边框和外边距)
clientWidth = content-width + padding-width
clientWidth用于获取dom元素在浏览器的实际占用文档的宽度6、布局
7、利用html css 编写样式,div垂直body居中、div内的text垂直居中,div高度等于body宽度的一半
<body> <div>垂直居中</div> </body> /* 方法一 */ body { position: relative; height: 100vh; } div { position: absolute; top: 50%; width: 100%; height: 0; padding: 25% 0; transform: translateY(-50%); text-align: center; background: green; } /* 方法二 */ body { height: 100vh; } body, div { display: flex; align-items: center; } div { width: 100%; padding: 25% 0; text-align: center; background: green; }
8、文本溢出省略效果
/* 超出一行用省略号显示 */ .title{ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } /* 超出2行用省略号显示 */ .title{ overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; text-overflow: ellipsis; }
9、1像素问题
使用css3的 scaleY(0.5) 来解决
eg:div的border-top的1px问题解决。.border-top-1px { position: relative; } .border-top-1px::before { content: ""; display: block; position: absolute; left: 0; top: 0; right: 0; transform-origin: 0 top; border-top: 1px solid #ccc; } @media (min-resolution: 2dppx) { .border-top-1px::before { transform: scaleY(.5); } } @media (min-resolution: 3dppx) { .border-top-1px::before { transform: scaleY(.333); } }
10、搞懂word-break、word-wrap、white-space
- white-space: 控制空白字符的显示,同时还能控制是否自动换行
1) white-space: nowrap; // 不换行
2) white-space: pre-wrap; // 保留空格和换行符,且可以自动换行
3) white-space: pre-line; // 空格被合并,换行符存在 - word-break: 控制单词如何被拆分换行
1) word-break: keep-all; // 所有“单词”一律不拆分换行。注意: “单词”包括连续的中文字符(还有日文、韩文等),或者可以理解为只有空格可以触发自动换行
2) word-break: break-all; // 所有单词碰到边界一律拆分换行。(不管是incomprehensibilities这样一行都显示不下的单词,还是long这样很短的单词,只要碰到边界,都会被强制拆分换行。用word-break: break-all时要慎重。) - word-wrap(overflow-wrap): 控制长度超过一行的单词是否被拆分换行
1)word-wrap: break-word;
// 只有当一个单词一整行都显示不下时,才会拆分换行该单词。
JS
变量类型和计算
1、typeof能判断哪些类型
- 识别所有值类型:undefined、String、Number、Boolean、Symbol、BigInt
- 识别函数:Function
判断是否是引用类型(但不可再细分)
// 判断函数 typeof function () {} // function typeof null; // object typeof [1, 2]; // object
2、何时使用 === 何时使用 ==
除了 == null
之外,其他都一律用 ===
obj.a == null 等价于 obj.a === null || obj.a === undefined的简写形式
3、值类型和引用类型的区别
1) 存储
值类型:在栈中存储
引用类型:在堆和栈中存储 // 栈中存储的是引用类型的内存地址
2) 常见值类型
undefined、String、Number、Boolean、Symbol、BigInt
3) 常见引用类型
Object、Array、null、Function
null:特殊引用类型,指针指向为空地址
function fn() {} // 特殊引用类型,但不用于存储数据,所以没有"拷贝、复制函数"这一说
4、手写深拷贝
/**
* 深拷贝
* @param {Object} obj 要拷贝的对象
*/
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
return obj; // obj是null, 或者不是对象和数组,直接返回
}
// 初始化返回结果
let result = obj instanceof Array ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // 保证key不是原型的属性
result[key] = deepClone(obj[key]); // 递归
}
}
// 返回结果
return result;
}
5、变量计算 - 类型转换
1) 字符串拼接
const b = 100 + '10'; // '10010'
const c = true + '10'; // 'true10'
2) ==
100 == '100'; // true
0 == ''; // true
0 == false; // true
false == ''; // true
null == undefined; // true
3) if语句和逻辑运算
- truly变量:!!a === true的变量
- falsely变量:!!a === false的变量。0, NaN, '', null, undefined, false
6、JS中哪些内置函数 -- 数据封装类对象
Object、Array、Boolean、Number、String、Function、Date、RegExp、Error
7、如何理解JSON
JSON是一种数据格式,一个JS对象。
JSON.stringify({a: 100, b: 20}); // 把对象变成字符串
JSON.parse('{"a": 100, "b": 20}'); // 把字符串变成对象
原型和原型链
1、如何判断一个变量是不是数组
a instanceof Array // instanceof用于判断引用类型属于哪个构造函数的方法
f instanceof Foo的判断逻辑是:
- f的__proto__一层一层往上,能否对应到Foo.prototype
再试着判断f instanceof Object
[] instanceof Array; // true [] instanceof Object; // true {} instanceof Object; // true
2、手写一个简易的jQuery,考虑插件和扩展性
class jQuery { constructor(selector) { const result = document.querySelectorAll(selector); // dom查询 const length = result.length; // 类数组 for (let i = 0; i < length; i++) { this[i] = result[i]; } this.length = length; this.selector = selector; } get(index) { return this[index]; } each(fn) { for (let i = 0; i < this.length; i++) { const elem = this[i]; fn(elem); } } on(type, fn) { return this.each(elem => { elem.addEventListener(type, fn, false); }) } // 扩展更多 DOM API } // 插件 jQuery.prototype.dialog = function(info) { alert(info); } // 造轮子 class myJQuery extends jQuery { // 继承 constructor(selector) { super(selector); } // 扩展自己的方法 addClass(className) { // dosth } }
3、描述new一个对象的过程
- 创建一个新对象
- this指向这个新对象
- 执行代码,即对this赋值
- 返回this // return this
4、描述new一个对象的过程
// 父类
class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`${this.name} say sth`);
}
}
// 子类
class Student extends Person {
constructor(name, number) {
super(name);
this.number = number;
}
toSchool() {
console.log(`姓名 ${this.name}, 学号 ${this.number}`);
}
}
class Teacher extends Person {
constructor(name, major) {
super(name);
this.major = major;
}
teach() {
console.log(`${this.name} 教授 ${this.major}`);
}
}
const lily = new Student('莉莉', 7); // 实例
console.log(lily.name, lily.number);
原型
class 实际上是函数,是语法糖
typeof Person; // function
typeof Student; // function
隐式原型和显示原型
lily.__proto__ // 隐式原型 Person
Student.prototype // 显示原型 Person
lily.__proto__ === Student.prototype // true
原型关系
- 每个class都有显示原型prototype
- 每个实例都有隐式原型__proto__
- 实例的__proto__指向对应class的prototype
基于原型的执行规则
- 获取属性lily.name 或执行方法lily.sayHi()时
- 先在自身属性和方法寻找
- 如果找不到则自动去__proto__中查找
原型链
Student.prototype.__proto__ // Person.prototype
Person.prototype === Student.prototype.__proto__
原型规则
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了"null"以外)。
- 所有的引用类型(数组、对象、函数),都有一个__proto__属性,属性值是一个普通的对象。// __proto__: 隐式原型
- 所有的函数,都有一个prototype属性,属性值也是一个普通的对象。 // prototype: 显示原型
- 所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的"prototype"属性值。
- 当试图得到一个对象的某个属性时,若该对象本身没有该属性,则会去它的__proto__(即它的构造函数的prototype)中寻找。
作用域和闭包
1、this的不同应用场景,如何取值
this的取值在函数执行
时确定
1) 当做普通函数被调用(this的值是window)
2) 使用call、apply、bind 被调用
3) 作为对象方法被调用
4) 在class方法中调用
5) 箭头函数: 箭头函数的this取其上级作用域的this值
2、手写bind函数
Function.prototype.bind1 = function() {
// 将参数解析为数组
const args = Array.prototype.slice.call(arguments);
// 获取this(取出数组第一项,数组剩余的就是传递的参数)
const t = args.shift(); // 调用时bind参数的第一项,即this的值
const _this = this; // 当前函数, eg: fn1.bind(...) 中的 fn1
// 返回一个函数
return function() {
// 执行原函数,并返回结果
return _this.apply(t, args);
}
}
3、作用域和自由变量
1) 作用域:变量的使用范围
- 全局作用域
- 函数作用域
- 块级作用域(ES6新增)
2) 自由变量:
- 一个变量在当前作用域没有定义,但被使用了
- 向上级作用域,一层一层依次寻找,直至找到为止
- 如果到了全局作用域都没找到,则报错 xx is not defined
4、闭包
闭包:作用域应用的特殊情况,有两种表现:
- 函数作为参数被传递
函数作为返回值被返回
// 函数作为参数 function print(fn) { let a = 200; fn(); } let a = 100; function fn() { console.log(a); } print(fn); // 100 // 函数作为返回值 function create() { let a = 100; return function() { console.log(a); } } let fn = create(); let a = 200; fn(); // 100
所有的自由变量的查找,是在
函数定义
的地方,向上级作用域查找
闭包的影响:变量会常驻内存,得不到释放。闭包不要乱用
5、实际开发中闭包的应用场景,举例说明
1) 隐藏数据
2) 做一个简单的cache工具
// 闭包隐藏数据,只提供API
function createCache() {
const data = {}; // 闭包中的数据,被隐藏,不被外界访问
return {
set(key, val) {
data[key] = val;
},
get(key) {
return data[key];
}
}
}
const c = createCache();
c.set('a', 100);
console.log(c.get('a'));
异步
1、单线程和异步
- JS是单线程语言,只能同时做一件事
- 浏览器和node.js已经支持JS启动进程,如Web Worker
- JS和DOM渲染共用同一个线程,因为JS可修改DOM结构
- 遇到等待(网络请求,定时任务)不能卡住
- 需要异步
- 回调callback函数形式
2) 同步和异步的区别
- 异步基于JS是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
2、手写Promise加载一张图片
function loadImg(src) {
const promise = new Promise((resolve, reject) => {
const img = document.createElement('img');
img.onload = () => {
resolve(img);
}
img.onerror = () => {
const err = new Error(`图片加载失败${src}`);
reject(err);
}
img.src = src;
});
return promise;
}
const url = 'xxx.png';
loadImg(url).then(img => {
console.log(img.width);
return img;
}).then(img => {
console.log(img.height);
}).catch(err => {
console.error(err);
})
3、前端使用异步的场景有哪些
- 网络请求,如ajax、图片加载
- 定时任务,如setTimeout
4、callback hell(回调地狱) 和 Promise
1) callback hell
// 获取第一份数据
$.get(url1, (data1) => {
console.log(data1);
// 获取第二份数据
$.get(url2, (data2) => {
console.log(data2);
// more data
...
})
})
2) Promise
function getData(url) {
return new Promise((resolve, reject) => {
$.ajax({
url,
success(data) {
resolve(data);
},
error(err) {
reject(err);
}
})
})
}
const url1 = '/data1.json';
const url2 = '/data2.json';
const url3 = '/data3.json';
getData(url1).then(data1 => {
console.log(data1);
return getData(url2);
}).then(data2 => {
console.log(data2);
return getData(url3);
}).then(data3 => {
console.log(data3);
}).catch(err => {
console.error(err);
})
DOM
1、DOM性能
- DOM操作非常"昂贵", 避免频繁的DOM操作
对DOM查询做缓存
const pList = document.getElementsByTagName('p'); // 缓存DOM查询 const pLength = pList.length; for (let i = 0; i < pLength; i++) { // 缓存length,只进行一次DOM查询 }
将频繁操作改为一次性操作
const list = document.getElementById('list'); // 创建一个文档片段,此时还没有插入到DOM树中 const frag = document.createDocumentFragment(); // 执行插入 for (let i = 0; i < 10; i++) { const li = document.createElement('li'); li.innerHTML = 'item ' + i; frag.appendChild(li); } // 都完成之后,再插入到DOM树中 list.appendChild(frag);
事件绑定
1、编写一个通用的事件监听函数
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector;
selector = null;
}
elem.addEventListener(type, e => {
const target = e.target;
if (selector) { // 需要代理
if (target.matches(selector)) {
fn.call(target, e);
}
} else {
fn.call(target, e); // 不需要代理
}
})
}
2、事件代理
- 代码简洁
- 减少浏览器内存占用
- 不要滥用
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
事件捕获(event capturing): 当鼠标点击或者触发dom事件时(被触发dom事件的这个元素被叫作事件源),浏览器会从根节点到事件源(由外到内)进行事件传播。
事件冒泡(dubbed bubbling):事件冒泡刚好相反,事件源到根节点(由内到外)进行事件传播。
DOM标准事件流的触发的先后顺序为:先捕获再冒泡,即当触发DOM事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。
element.addEventListener(event, function, useCapture);
将多个子元素上相同的事件添加到父元素上,eg: 将li的事件代理到ul上。
3、无限下拉的图片列表,如何监听每个图片的点击
- 事件代理
- 用e.target获取触发元素
- 用matches来判断是否是触发元素
ajax
1、手写一个简易的ajax
function ajax(url) {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText));
} else if (xhr.status === 404){
reject(new Error('404 not found'));
}
}
}
xhr.send(null);
})
return p;
}
ajax(url).then(res => {
console.log(res);
}).catch(err => {
console.error(err);
})
2、XMLHttpRequest
1) get请求
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api', true);
xhr.onreadystatechange = function() {
// 异步执行
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
}
}
}
xhr.send(null);
2) post请求
const xhr = new XMLHttpRequest();
xhr.open('POST', '/login', true);
xhr.onreadystatechange = function() {
// 异步执行
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
}
}
}
const params = {
userName: 'liz',
pwd: '1234'
}
xhr.send(JSON.stringify(params));
3、状态码
1) xhr.readyState
0 - (未初始化)还没有调用send()方法
1 - (载入)已调用send()方法,正在发送请求
2 - (载入完成)send()方法执行完成,已经接收到全部相应内容
3 - (交互)正在解析响应内容
4 - (完成)响应内容解析完成,可以在客户端调用
2) xhr.status
2xx - 表示成功处理请求,如200
3xx - 需要重定向,浏览器直接跳转,如301 302 304
301 - 永久重定向
302 - 临时重定向
304 - 资源未改变,浏览器利用缓存的资源
4xx - 客户端请求错误,如404 403
404 - 请求地址有错误
403 - 客户端没有权限
5xx - 服务器端错误
4、跨域
浏览器的同源策略(服务端没有同源策略)
1) 什么是跨域(同源策略)
- ajax请求时,浏览器要求当前网页和server必须同源(安全)
- 同源:协议、域名、端口,三者必须一致
- 加载图片、css、js可无视同源策略
<img src="跨域的图片地址" />
<link href="跨域的css地址" /> eg: 引用cdn的地址
<script src="跨域的js地址"></script>
<img />可用于统计打点,可使用第三方统计服务
<link />、<script>可使用CDN,CDN一般都是外域
<script>可实现JSONP
- 跨域
所有的跨域都必须经过server端允许和配合
未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
2) JSONP
- <script>可绕过跨域限制
- 服务器可以任意动态拼接数据返回
- 所以,<script>就可以获得跨域的数据,只要服务端愿意返回
3) CORS(服务端支持)
服务器端设置http header
response.setHeader('Access-Control-Allow-Origin', '允许跨域的域名称');
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With');
response.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
// 接收跨域的cookie
response.setHeader('Access-Control-Allow-Credentials', 'true');
5、跨域的常用实现方式
跨域指浏览器不能执行其他网站的脚本。跨域是浏览器的同源策略限制造成的,是浏览器对js施加的安全限制。(同源策略限制)
JSONP: 利用<script>标签不受跨域限制的特点,缺点是只能支持 get 请求
function jsonp(url, jsonpCallback, success) {
const script = document.createElement('script');
script.src = url;
script.async = true;
script.type = 'text/javascript';
window[jsonpCallback] = function(data) {
success && success(data);
}
document.body.appendChild(script);
}
JSONP跨域的基本原理:由于script 标签不受浏览器同源策略的影响,允许跨域引用资源。因此,通过动态创建 script 标签,然后利用 src 属性进行跨域。
设置 CORS:
Access-Control-Allow-Origin:* // CORS(跨域资源共享): 服务端设置Access-Control-Allow-Origin
postMessage
window.postMessage() 是HTML5的一个API,专注实现不同窗口不同页面的跨域通讯。
其他
1、JS开发技巧
格式化金钱
formatRMB(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
最快捷的数组求最大值
const arr = [ 1,5,1,7,5,9];
Math.max(...arr) // 9
最短的数组去重写法
[...new Set([2,"12",2,12,1,2,1,6,12,13,6])]
// [2, "12", 12, 1, 6, 13]
2、判断if([] == false) {} , if({} == false) {} , if([]) {}
若使用===,表示绝对相等,需要类型相同并且值相同;若使用==,则在类型不同的情况下会进行类型转换,然后再比较。
JS“假值”总共只有6个:false,undefined,null,0,''(空字符串),NaN
- []直接用于if判断条件时会被转化为true。
- 与布尔值比较,都会将两边的值转化为Number,[]转换为0,{}转换为NaN。
1) [] == false // true
2) {} == false // false
3) [] // true
尽量避免使用==
和!=
运算符
3、window的onload事件和domcontentloaded谁先谁后?
页面加载事件:DOMContentLoaded和onLoad
1) DOMContentLoaded:当DOM树生成后触发该事件。
2) onLoad:当页面的所有资源(css、js、图片、视频等)都加载完成后触发。
DOM完整的解析过程:
- 解析HTML结构。
- 加载外部脚本和样式表文件。
- 解析并执行脚本代码。// js之类的
- DOM树构建完成。// DOMContentLoaded事件会被触发
- 加载图片等外部文件。
- 页面加载完毕。//load事件会被触发
// todo:
参考文章:
Flex 布局教程:语法篇
中高级前端大厂面试秘籍,为你保驾护航金三银四,直通大厂(上)
CSS盒模型与style.width,offsetWidth,clientWidth的关系
字节跳动最爱考的前端面试题:CSS 基础
[浅谈JavaScript空数组[]和空对象{}的布尔运算](https://blog.csdn.net/zhouziy...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。