【For Interview】琐碎必考题

149 阅读6分钟

清除浮动

参考文章: 如何清除浮动
下面是自己的理解: 究竟什么是清除浮动?为什么要清除? 如下案例,父元素是container,高度没设置,子元素是3个浮动元素,可以看到子元素撑破了父级元素。
所以清除浮动的意思在这里就是,让父级元素能够涵盖住子级元素

  1. 最简单粗暴-直接给父级元素增加高度 设置为 150px ,缺点也很明显,没办法复用

  2. 简单易行-添加冗余子元素 记住一个要点:在父级元素末尾添加的元素必须是一个块级元素,否则无法撑起父级元素高度

  3. 通用的方法-伪元素清除 因为上面那个方法需要新增一个新的dom节点,现在不想新增,所以考虑伪元素 注意伪元素怎么写,visibility表示浏览器可以渲染出来但是不显式出来

  4. 将父元素转化为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可以包含浮动,这可以清除浮动。

  1. <br>标签清除浮动

BFC(Block Formatting Contexts)

在清除浮动章节也有遇到,可以参考这个文章10 分钟理解 BFC 原理 最主要看如何触发BFCBFC特性及应用

  1. BFC下边距重叠解决方法 可以看到margin都是50px,本来想要的是上下方块100px间隔,实际只有50px,原因是两个块都在同一个BFC下面,解决方案是把两块放在不同BFC内 新创建outer元素设置为overflow: hidden;
  2. 上下边距重叠解决方法 如图所示,丢失了margin-top10px,因为它们在同一个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]'

其他

自己容易搞混的东西

  1. 函数变量解析 当不知道参数输入多少个的时候
function test(...args) {
   console.log(args);
   console.log(...args);
}
test(1, 2, 3, 4)

// [1, 2, 3, 4]
// 1, 2, 3, 4

记不住可以这么理解,...是解构,加上[]是整体,代码中...[]就是数组里面的内容展开了,单纯一个args, 就是[]类型的数组