清除浮动
参考文章: 如何清除浮动
下面是自己的理解:
究竟什么是清除浮动?为什么要清除?
如下案例,父元素是container,高度没设置,子元素是3个浮动元素,可以看到子元素撑破了父级元素。
所以清除浮动的意思在这里就是,让父级元素能够涵盖住子级元素
-
最简单粗暴-直接给父级元素增加高度 设置为 150px ,缺点也很明显,没办法复用
-
简单易行-添加冗余子元素 记住一个要点:在父级元素末尾添加的元素必须是一个块级元素,否则无法撑起父级元素高度
-
通用的方法-伪元素清除 因为上面那个方法需要新增一个新的dom节点,现在不想新增,所以考虑伪元素 注意伪元素怎么写,
visibility表示浏览器可以渲染出来但是不显式出来 -
将父元素转化为BFC 这个最简单,直接修改父级元素,让父级元素变为
块级元素上下文,除了使用下图的属性,还可以用
- float 为 left | right
- overflow 为 hidden | auto | scorll
- display 为 table-cell | table-caption | inline-block
- position 为 absolute | fixed
我挨个试了一下,罗列最常见的
这里可以给父元素设置overflow:auto,但是为了兼容IE最好使用overflow:hidden。
但这种办法有个缺陷:如果有内容出了盒子,用这种方法就会把多的部分裁切掉,所以这时候不能使用。
BFC的主要特征:
-
BFC容器是一个隔离的容器,和其他元素互不干扰;所以我们可以用触发两个元素的BFC来解决垂直边距折叠问题。
-
BFC不会重叠浮动元素
-
BFC可以包含浮动,这可以清除浮动。
<br>标签清除浮动
BFC(Block Formatting Contexts)
在清除浮动章节也有遇到,可以参考这个文章10 分钟理解 BFC 原理
最主要看如何触发BFC和BFC特性及应用
- BFC下边距重叠解决方法
可以看到margin都是50px,本来想要的是上下方块100px间隔,实际只有50px,原因是两个块都在同一个BFC下面,解决方案是把两块放在不同BFC内
新创建
outer元素设置为overflow: hidden; - 上下边距重叠解决方法
如图所示,丢失了
margin-top的10px,因为它们在同一个BFC内(body),同一个BFC内就会产生margin collapse,所以解决方法就是让它们在不同的BFC添加
overflow属性最简单hack一点的方法是了解为啥会这样:
如果块元素的 margin-top 与它的第一个子元素的 margin-top 之间没有 border、padding、inline content、 clearance 来分隔,或者块元素的 margin-bottom 与它的最后一个子元素的 margin-bottom 之间没有 border、padding、inline content、height、min-height、 max-height 分隔,那么外边距会塌陷。子元素多余的外边距会被父元素的外边距截断。
添加个不显眼的margin或者padding就好了
用js实现new
参考文章: JavaScript深入之new的模拟实现
new做了什么?
- 创建一个新的对象
- 继承父类原型上的方法. (就是父类的prototype里面的方法)
- 添加父类的属性到新的对象上并初始化. 保存方法的执行结果.
- 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象。
实现方案
// 参考中给的 初级版本
function objectFactory() {
var obj = new Object();
// 这个完全是为了取到构造函数
Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
Constructor.apply(obj, arguments);
return obj;
};
// 自己改的容易记住的版本
function objectFactory(inputObj, ...args) {
var obj = new Object();
obj.__proto__ = inputObj.prototype;
inputObj.apply(obj, args);
return obj;
};
以前一直有个概念不理解,就是__proto__和prototype是啥关系。
后来整明白了。
prototype: 是构造函数里面的东西,将来你new出来的实例一定会出现在实例的__proto__里面。
__proto__: 是实例里面的(其实只是相对这么说)。它指向构造函数的prototype,举例说明:
function Otaku(name, age) {
this.name = name;
this.age = age;
this.habit = 'Games';
}
Otaku.prototype.strength = 60;
Otaku.prototype.sayYourName = function() {
console.log('I am ' + this.name);
}
function objectFactory(inputObj, ...args) {
var obj = new Object();
obj.__proto__ = inputObj.prototype;
// 这一行直接跟es5继承那个借用构造函数很相似,也可写为
// inputObj.call(obj, ...args)
inputObj.apply(obj, args);
return obj;
};
var person = objectFactory(Otaku, 'Kevin', '18')
把 person打印出,可以看到实例的__proto__和构造函数的prototype是一样的(废话,都是指向它么)
注意,还有一个关系
person.constructor === Otaku
表达式是true,因为实例的构造函数就是Otaku,这个没有啥疑问。
回到标题来,new完了吗?
没有!
文中提到了构造函数返回值的问题。
/**
* 真实的new情况:
* 返回了对象,只能访问return的属性
*/
function Otaku (name, age) {
this.strength = 60;
this.age = age;
return {
name: name,
habit: 'Games'
}
}
var person = new Otaku('Kevin', '18');
console.log(person.name) // Kevin
console.log(person.habit) // Games
console.log(person.strength) // undefined
console.log(person.age) // undefined
/**
* 另一真实new种情况:
* 返回了普通类型的
* 只能访问内部属性
*/
function Otaku (name, age) {
this.strength = 60;
this.age = age;
return 'handsome boy';
}
var person = new Otaku('Kevin', '18');
console.log(person.name) // undefined
console.log(person.habit) // undefined
console.log(person.strength) // 60
console.log(person.age) // 18
所以采用了这个策略:
判断返回的值是不是一个对象,如果是一个对象,我们就返回这个对象,如果没有,我们该返回什么就返回什么
function objectFactory(inputObj, ...args) {
var obj = new Object();
obj.__proto__ = inputObj.prototype;
var ret = inputObj.apply(obj, args);
return typeof ret === 'object' ? ret : obj;
};
节流防抖
概念:
防抖: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
节流: 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
代码实现:
// 防抖
function debounce(fun, delay) {
return function(...args) {
clearTimeout(fun.id);
fun.id = setTimeout(() => {
fun.apply(this, args);
}, delay)
}
}
// 节流
// 可以看到节流根本不需要定时器,核心就是计算时间间隔
function throttle(fn, delay) {
var last = 0;
return function() {
const now = Date.now();
if (now - last >= delay) {
fn.apply(this, arguments);
last = now;
}
}
}
判断数组、对象、函数
- 判断是否数组
首先
typeof肯定不对,返回的是'Object'
// 第一种
const arr = [1, 2, 3];
console.log(arr instanceof Array); //true
// 第二种 原型链方法
console.log(arr.__proto__.constructor === Array); //true
console.log(arr.constructor === Array); //true
// 第三种 最通用的
console.log(Object.prototype.toString.call(arr)); //"[object Array]"
// 第四种 最新的
console.log(Array.isArray(arr)); //true
- 判断是否是函数 这个没有过多解释的
var a = () => {};
function b() {
}
console.log(a, b); //'function' 'function'
- 判断是否对象
console.log(Object.prototype.toString.call(obj)); //'[Object Object]'
其他
自己容易搞混的东西
- 函数变量解析 当不知道参数输入多少个的时候
function test(...args) {
console.log(args);
console.log(...args);
}
test(1, 2, 3, 4)
// [1, 2, 3, 4]
// 1, 2, 3, 4
记不住可以这么理解,...是解构,加上[]是整体,代码中...[]就是数组里面的内容展开了,单纯一个args, 就是[]类型的数组