Code Monkey home page Code Monkey logo

frontend's People

Contributors

438198602 avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

yinfanfan

frontend's Issues

.editorconfig

root = true

[*]
# 缩进样式 空格
indent_style = space
# 缩进宽度 4个空格
indent_size = 4
# 字符编码 utf-8
charset = utf-8
# 换行符使用 unix 的换行符 \n
end_of_line = lf
# 行尾允许空格
trim_trailing_whitespace = true
# 文件末尾加一个空行
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false        # .md 文件不去掉每行末尾的空格

网页中图片加载失败或没有图片的CSS处理方法

当图片加载失败或者没有图片的时候,使用伪元素来显示默认图片

img 标签必须有 alt 属性,否则 Firefox 浏览器不生效

img {
    display: inline-block;
    position: relative;
    width: 540px;
    height: 258px;
    background: #ccc;
    vertical-align: top;
}
img:after {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background: url('https://www.baidu.com/img/bd_logo1.png') #ccc;
}

react、vue的Virtual DOM结构

一、vue

https://www.jianshu.com/p/9db8eb16d76f
https://juejin.im/post/5b86f6cc5188256fd44c0ce9

vue的createElement 返回 VNode 对象。

// VNode
export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node

  // strictly internal
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?
  asyncFactory: Function | void; // async component factory function
  asyncMeta: Object | void;
  isAsyncPlaceholder: boolean;
  ssrContext: Object | void;
  fnContext: Component | void; // real context vm for functional nodes
  fnOptions: ?ComponentOptions; // for SSR caching
  fnScopeId: ?string; // functioanl scope id support

  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag // 当前节点标签名
    this.data = data // 当前节点数据(VNodeData类型)
    this.children = children // 当前节点子节点
    this.text = text // 当前节点文本
    this.elm = elm // 当前节点对应的真实DOM节点
    this.ns = undefined // 当前节点命名空间
    this.context = context // 当前节点上下文
    this.fnContext = undefined // 函数化组件上下文
    this.fnOptions = undefined // 函数化组件配置项
    this.fnScopeId = undefined // 函数化组件ScopeId
    this.key = data && data.key // 子节点key属性
    this.componentOptions = componentOptions // 组件配置项 
    this.componentInstance = undefined // 组件实例
    this.parent = undefined // 当前节点父节点
    this.raw = false // 是否为原生HTML或只是普通文本
    this.isStatic = false // 静态节点标志 keep-alive
    this.isRootInsert = true // 是否作为根节点插入
    this.isComment = false // 是否为注释节点
    this.isCloned = false // 是否为克隆节点
    this.isOnce = false // 是否为v-once节点
    this.asyncFactory = asyncFactory // 异步工厂方法 
    this.asyncMeta = undefined // 异步Meta
    this.isAsyncPlaceholder = false // 是否为异步占位
  }

  // 容器实例向后兼容的别名
  get child (): Component | void {
    return this.componentInstance
  }
}

其实就是一个普通的 JavaScript Class 类,中间有各种数据用于描述虚拟 DOM。

二、react

https://juejin.im/post/5cab1fb8e51d452b4234d277

class Element {
  constructor(type, attr, children) {
    this.type = type;
    this.attr = attr;
    this.children = children;
    /*this.props={...attr,children:children}*/
  }
  render() {
    // 这个方法将虚拟的DOM转成真实的DOM;
    let ele = document.createElement(this.type);
    // 2. 设置行间属性
    for (let key in this.attr) {
      let _key = key;
      if (key === "className") {
        _key = "class";
      }
      if (key === "htmlFor") {
        _key = "for";
      }
      ele.setAttribute(_key, this.attr[key]);
    }
    // 3.children
    this.children.forEach(item => {
      // 如果数组中的成员是Element的实例,需要继续调用render方法;将虚拟的DOM转成真实的DOM;
      // 循环子节点,都放入ele中;
      let curEle =
        item instanceof Element
          ? item.render()
          : document.createTextNode(item);
      ele.appendChild(curEle);
    });
    return ele; // 将创建的元素转成DOM返回;
  }
}
let obj = {
  createElement(type, attr, ...children) {
    // type: 元素类型
    // attr:行间属性
    // children : 子节点 ... 把多余的参数放进一个数组中;
    return new Element(type, attr, children);
  }
};
let objDOM = {
  render(element, container) {
    // container : 容器,根元素;
    // element: 虚拟的DOM对象;当render执行时,让这个虚拟的DOM转成真实的DOM;
    container.appendChild(element.render());
  }
};
let y = obj.createElement("div", { a: 1, className: 12 }, "**北京");
objDOM.render(y, document.getElementById("root"));

最终render结果

<div a="1" class="12">**北京</div>

Javascript中的apply与call的区别

apply和call两者在作用上是相同的,但两者在参数上有区别的。

第一个参数意义都一样。第二个参数:apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。
例如 func.call(func1,var1,var2,var3) 对应的apply写法为:func.apply(func1,[var1,var2,var3]),同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入。

使用 setTimeout 模拟 setInterval

setInterval 的缺点,使用 setTimeout 模拟 setInterval

在开发商城项目的时候,会遇到这样的场景:用户创建了订单之后,前端开发需要一直轮询来知道用户是否支付。这时候大多数人的第一想法就是,哦,这个简单,setInterval就解决了。但 setInterval 真的完美吗。

我们来看一个例子:

let startTime = new Date().getTime(),
    count = 0;
// 模拟耗时任务
setInterval(function() {
  let i = 0;
  while (i++ < 1000000000);
}, 0);
setInterval(function() {
  count++;
  console.log("间隔:", new Date().getTime() - (startTime + count * 1000), "毫秒");
}, 1000);

结果:

间隔: 852 毫秒
间隔: 1098 毫秒
间隔: 1950 毫秒
间隔: 2175 毫秒
间隔: 2980 毫秒
间隔: 3203 毫秒
间隔: 4035 毫秒
间隔: 4865 毫秒
间隔: 5102 毫秒
间隔: 6093 毫秒
间隔: 6976 毫秒
间隔: 7245 毫秒
间隔: 8050 毫秒
间隔: 8908 毫秒
间隔: 9131 毫秒
间隔: 10020 毫秒
间隔: 10882 毫秒
间隔: 11153 毫秒
...

可以看出,相差的时间是越来越大,越来越不准确,并且有时候两次执行间隔很短。

setInterval 时间不准确的原因

首先,我们要明白,定时器指定的时间间隔,表示的是何时将定时器的代码添加到消息队列,而不是何时执行代码。所以真正何时执行代码的时间是不能保证的,取决于何时被主线程的事件循环取到,并执行。

setInterval(function, N)
// 即:每隔 N 秒把 function 事件推到消息队列中

假设定时器里面的代码需要进行大量的计算(耗费时间较长),或者是 DOM 操作。这样一来,花的时间就比较长,有可能前一次代码还没有执行完,后一次代码就被添加到队列了。这就导致定时器变得不准确,甚至出现同一时间执行两次的情况。

setInterval 缺点 与 setTimeout 的不同

注意:当使用 setInterval 时,仅当队列中没有该定时器代码实例时,才添加到任务队列中。

setInterval

所以,使用 setInterval 时可能会导致:

1、某些间隔会被跳过;

2、可能多个定时器会连续执行;

可以这么理解:每个 setTimeout 产生的任务会直接 push 到任务队列中;而 setInterval 在每次把任务 push 到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中,如果有则不添加,没有则添加)。

因而我们一般用 setTimeout 模拟 setInterval ,来规避掉上面的缺点。

在前一个定时器执行完前,不会向队列插入新的定时器(解决缺点一)

保证定时器间隔(解决缺点二)

上面的例子就可以改为:

let startTime = new Date().getTime(),
    count = 0;
// 模拟耗时任务
setInterval(function() {
  let i = 0;
  while (i++ < 1000000000);
}, 0);
const repeat = (func, ms) => {
  setTimeout(() => {
    func();
    repeat(func, ms);
  }, ms);
};
repeat(() => {
  count++;
  console.log("间隔:", new Date().getTime() - (startTime + count * 1000), "毫秒");
}, 1000);

移动端的滚动穿透

监听 touchmove 事件,阻止其默认操作 event.preventDefault()

vue
@touchmove.prevent

CSS属性最佳书写顺序

CSS属性最佳书写顺序

相信前端开发者们都听说过CSS属性书写顺序这种说法,那你知道CSS属性的最佳书写顺序?为什么要这样写吗?

CSS属性最佳书写顺序

定位属性:position、display、float、left、top、right、bottom、overflow、clear、z-index

自身属性:width、height、padding、border、margin、background

文字样式:font-family、font-size、font-style、font-weight、color

文本属性:text-align、vertical-align、text-wrap、text-transform、text-indent、text-decoration、letter-spacing、word-spacing、white-space、text-overflow

css3中新增属性:content、box-shadow、border-radius、transform

原因

减少浏览器reflow(回流),从而提升浏览器渲染dom的性能。

详细点就是:

1、解析html构建dom树,解析css构建css树:将html解析成树形的数据结构,将css解析成树形的数据结构。

2、构建render树:DOM树和CSS树合并之后形成的render树。

3、布局render树:有了render树,浏览器已经知道网页中有哪些节点,各个节点的css定义以及它们的从属关系,从而计算出每个节点在屏幕中的位置。

4、绘制render树:按照计算出来的规则,通过显卡把内容画在屏幕上。

css样式解析到显示至浏览器屏幕上就发生在 2、3、4 步骤,可见浏览器并不是一获取到css样式就立马开始解析而是根据css样式的书写顺序将之按照dom树的结构分布render样式,完成第 2 步,然后开始遍历每个树结点的css样式进行解析,此时的css样式的遍历顺序完全是按照之前的书写顺序。

在解析过程中,一旦浏览器发现某个元素的定位变化影响布局,则需要倒回去重新渲染。正如按照这样的书写书序:

width: 100px;
height: 100px;
position: absolute;

当浏览器解析到 position 的时候突然发现该元素是绝对定位元素需要脱离文档流,而之前却是按照普通元素进行解析的,所以不得不重新渲染,解除该元素在文档中所占位置,然而由于该元素的占位发生变化,其他元素也可能会受到它回流的影响而重新排位。最终导致 3 步骤花费的时间太久而影响到 4 步骤的显示,影响了用户体验。

使用Typescript给JavaScript做静态类型检查

参考链接:https://hateonion.me/posts/aeb8/

// 对 JavaScript 文件使用 TypeScript 的 Type Checking(类型检查)
// 下面这行绝对不能删除
// @ts-check


// 对变量进行类型检查
/**
 * @type {number}
 */
let x;
x = 0;


// 对函数进行类型检查
/**
 * @param {string} foo
 * @param {string} bar
 * @returns {string}
*/
function test1(foo, bar) {
    return `${foo} and ${bar}`;
}

类似 TypeScript 的写法

// @ts-check
/**
 * @typedef Example1
 * @prop {string} street 街道
 * @prop {string} city 城市
 * @prop {number} zip 邮政编码
 *
 * @typedef Example2
 * @prop {string} name
 * @prop {string} email
 * @prop {Boolean} isShow
 */

/** @type {Example1} */
const data1 = {
    street: '街道',
    city: 100
};

/** @type {Example1 & Example2} */
const data2 = {
    street: '街道',
    city: 100,
    name: '001',
    isShow: 2
};

parseInt() 的怪异行为

https://www.runoob.com/jsref/jsref-parseint.html

语法

parseInt(string, radix)
参数 描述
string 必需。要被解析的字符串。
radix 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。

定义和用法

parseInt() 函数可解析一个字符串,并返回一个整数。

parseInt() 的怪异行为

先来看几个例子:

console.log(parseInt(0.5));       // => 0
console.log(parseInt(0.05));      // => 0
console.log(parseInt(0.005));     // => 0
console.log(parseInt(0.0005));    // => 0
console.log(parseInt(0.00005));   // => 0
console.log(parseInt(0.000005));  // => 0
console.log(parseInt(0.0000005)); // => 5

最后一个为什么是 5 呢?

我们再看一看 parseInt 的第一个参数:如果它不是字符串,则将其转换为字符串,然后解析,并返回解析后的整数。

接下来,我们尝试将浮点数手动转换为字符串表示形式:

console.log(String(0.5));       // => '0.5'
console.log(String(0.05));      // => '0.05'
console.log(String(0.005));     // => '0.005'
console.log(String(0.0005));    // => '0.0005'
console.log(String(0.00005));   // => '0.00005'
console.log(String(0.000005));  // => '0.000005'
console.log(String(0.0000005)); // => '5e-7'

然后在使用 parseInt 解析,看下结果:

console.log(parseInt(0.0000005)); // => 5
console.log(parseInt(5e-7));      // => 5
console.log(parseInt('5e-7'));    // => 5

是不是发现问题了。因为 parseInt 始终将其第一个参数转换为字符串,所以小于10的-6次方的浮点数将以指数表示。然后 parseInt 从 float 的指数表示法中提取整数。

所以,为了安全地提取浮点数的整数部分,建议使用 Math.floor() 函数。

总结

parseInt 是将数字字符串解析为整数的函数。

使用 parseInt 提取浮点数的整数部分时必须小心。

小于10的-6次方 (例如0.0000005,也就是5*10-7) 的浮点数转换成字符串时被写成指数表示法 (例如 5e-7 是 0.0000005 的指数表示法)。这就是为什么在 parseInt 中使用这么小的浮点数会导致意想不到的结果:只有指数表记的重要部分(例如 5e-7 中的 5)会被解析。

JS中浮点数精度问题

参考链接:https://juejin.cn/post/6844903572979597319

使用加减乘除时偶尔会出现精度问题,如:

0.1 + 0.2 = 0.30000000000000004
0.3 - 0.2 = 0.09999999999999998
0.1 * 3 = 0.30000000000000004
0.3 / 0.1 = 2.9999999999999996

为什么

让我们来看一下为什么 0.1 + 0.2 会等于 0.30000000000000004,而不是 0.3。

在 JavaScript 中所有数字包括整数和小数都只有一种类型 Number。它的实现遵循 IEEE二进制浮点数算术标准(IEEE 754),使用64位固定长度来表示,也就是标准的 double 双精度浮点数。

首先,十进制的 0.1 和 0.2 会被转换成二进制的,但是由于浮点数用二进制表示时是无穷的:

0.1 -> 0.00011001100110011001100110011001100110011001100110011010
0.2 -> 0.0011001100110011001100110011001100110011001100110011010

IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持53位二进制位,所以两者相加之后得到二进制为:

0.0100110011001100110011001100110011001100110011001100111

因浮点数小数位的限制而截断的二进制数字,再转换为十进制,就成了0.30000000000000004。所以在进行算术计算时会产生误差。

解决方法

通常我们会使用 toFixed() 来解决,但其在常见浏览器上的表现不一致。

如:1.335.toFixed(2) 正常应该返回 1.34

浏览器 IE7-11 edge Firefox chrome
结果 1.34 1.33 1.33 1.33

测试结果只有 IE 符合预期。

针对 toFixed 的兼容性问题,我们可以把 toFixed 重写一下来解决。

我们通过判断最后一位是否大于等于5来决定需不需要进位,如果需要进位先把小数乘以倍数变为整数,加1之后,再除以倍数变为小数,这样就不用一位一位的进行判断。

// toFixed兼容方法
Number.prototype.toFixed = function(len) {
    if (len > 20 || len < 0) {
        throw new RangeError('toFixed() digits argument must be between 0 and 20');
    }
    // 对数字末尾加0
    function padNum(num) {
        num = num.toString();
        var dotPos = num.indexOf('.');
        if (dotPos === -1) {
            // 整数的情况
            num += '.';
            for (var i = 0; i < len; i++) {
                num += '0';
            }
            return num;
        } else {
            // 小数的情况
            var need = len - (num.length - dotPos - 1);
            for (var j = 0; j < need; j++) {
                num += '0';
            }
            return num;
        }
    }
    // .123 转为 0.123
    var number = Number(this);
    if (isNaN(number) || number >= Math.pow(10, 21)) {
        return number.toString();
    }
    if (typeof (len) == 'undefined' || len == 0) {
        return (Math.round(number)).toString();
    }
    var result = number.toString(),
        numberArr = result.split('.');

    if (numberArr.length < 2) {
        // 整数的情况
        return padNum(result);
    }
    var intNum = numberArr[0], // 整数部分
        deciNum = numberArr[1], // 小数部分
        lastNum = deciNum.substr(len, 1); // 最后一个数字
    
    if (deciNum.length == len) {
        // 需要截取的长度等于当前长度
        return result;
    }
    if (deciNum.length < len) {
        // 需要截取的长度大于当前长度 1.3.toFixed(2)
        return padNum(result);
    }
    // 需要截取的长度小于当前长度,需要判断最后一位数字
    result = intNum + '.' + deciNum.substr(0, len);
    if (parseInt(lastNum, 10) >= 5) {
        // 最后一位数字大于5,要进位
        var times = Math.pow(10, len); // 需要放大的倍数
        var changedInt = Number(result.replace('.', '')); // 截取后转为整数
        changedInt++; // 整数进位
        changedInt /= times; // 整数转为小数,注:有可能还是整数
        result = padNum(changedInt + '');
    }
    return result;
}

js 自增(++)

自增(++)

一元运算符, 将操作数的值加一. 如果放在操作数前面 (++x), 则返回加一后的值; 如果放在操作数后面 (x++), 则返回操作数原值, 然后再将操作数加一.

var x = 3;
console.log(++x);
// 4
console.log(x);
// 4

var y = 3;
console.log(y++);
// 3
console.log(y);
// 4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.