Coder Social home page Coder Social logo

fe-knowledge-js's People

Contributors

vstar18 avatar

Watchers

 avatar  avatar

fe-knowledge-js's Issues

JS-a? if(a == 1 && a == 12)

这个问题涉及js的强制类型转换,详细内容可以查看《你不知道的JS》-中-强制类型

  • == 和=== 的区别

    • 并不是==比较值,===比较类型
    • 而是== 在比较的时候允许进行强制类型转换,===不允许
  • 而在进行强制类型转换时,如果操作值是对象,将遵循ToPrimitive抽象算法进行转换

    • 如果转换为number,则先调用valueOf,如果返回值不是基本类型值则
    • 调用toString

因此我们可以这样操作

var a = {
    value: 1,
    valueOf: function () {
      if(this.value == 1){
        this.value++;
        return 1;
      }
      if(this.value == 2){
        return 12;
      }
    }
}
if(a == 1 && a == 12) {
  console.log('happened')
}

JS-为什么有的编程规范要求用 void 0 代替 undefined

undefined类型表示的是未定义。其类型只有一个值,为undefined;
常和null进行对比说明,null是定义了但值为空。
但是JS标准在设计undefined的时候是有缺陷的,不同于其他基本数据类型,undefined在js中是一个变量,并不是一个关键字,也就是说是可以被更改的;
void可以转换任意表达式为undefined值,
所以有一些编程标准才会要求用void 0来代替undefined;

JS-类型判断

方法有三,各有长短


方法一:实际开发中比较常用的typeof

语法:

  1. typeof operand
    其中operand:表示对象或者原始类型的表达式,其类型会被返回;
类型 结果
String 'string'
Number 'number'
Boolean 'boolean'
Undefined 'undefined'
Null 'object'
Object 'object'
Array 'object'
Symbol 'symbol'
Funciton 'funciton'

可以看到typeof还是可以判断很多类型的,缺点在于不能区别null,array和object;

null也会被识别为object的原因是在最初实现js的时候,js中的值是由一个表示类型的标签和实际数据值表示的,对象的标签是0,由于null代表的是空指针,大多数平台下的值为0*00,所以null的类型标签为0,因此typeof null返回object;

  1. instanceof
    instanceof实际的用途是用来判断一个构造函数的prptotype是否出现在一个实例对象的原型链中
var a = new Object();
a instanceof Object //true
[] instanceof Object//true
[] instanceof Array//true
{} instanceof Object//true
{} instanceof Array //true
  • 缺点:判断的是一个实例,所以不能判断null,undefined,因为两者没有对应的对象类型
    也不能进行一下判断string,boolean同理
2 instanceof Number // false
new Number(2) instanceof Number //true
  1. toString
    Object上的toString方法一个表示该对象的字符串,[object,type],type是其类型。
    返回的是[[class]],而其值是无法改变的
Object.prototype.toString.call(2)//[object Number]

JS-[1,3,10].map(parseInt)输出什么

分析:map&parseInt

  • map
    语法
var new_array = arr.map(function callback( currentValue, index, array){
  //currentValue当前值
  //index当前值下标
  //array当前数组
  //thiaArgs callback执行时传入的this
  //返回新数组
},thisArgs)
注意点⚠️
1. map遍历只处理有值的下标,无值的将被忽略
2. map返回一个新的数组,所以在运用是如果不使用新数组时,不应该使用map,而是用forEach或者for-of替代
3. map不修改原数组
4. 通常情况下,map的callback只需要一个参数,就是当前值,但并不意味着只传递了一个,这是个思维惯性,注意奥
  • parseInt
    语法:将string转换成为radix进制的整数
parseInt(string,radix)
parseInt('123', 5) // 将'123'看作5进制数,返回十进制数38 => 1*5^2 + 2*5^1 + 3*5^0 = 38
注意:⚠️
1. 参数string是一个字符串,如果不是,会执行ToString操作
2. radix介于2-36之间(数学基础系统),超过这个数字的进制将都返回NaN,不指定的情况下默认为10进制
3. 如果string中有不在radix进制中的字符,其本身值及后续值将被忽略并返回目前位置解析的数值

  • 疑问:🤔️
  1. 为什么parseInt(0.00000000000000000000122434, 10)的结果是1呢
  2. parseInt("FXX123", 16)为什么是15呢
  3. parseInt("0e0",16)为什么是224呢
  4. parseInt(1,0)为什么是1呢,radix应该是介于2-36之间的数字才对啊-->0默认表示10进制

  • 结论:
    根据以上概念可知,[1,3,10].map(parseInt)实际上应该返回一个新的数组,数组成员是原数组成员经过parseInt(currentValue,index)返回的值;
    而parseInt(1,0)为NaN,0默认表示10进制,parseInt(3,1)为NaN,因为radix是介于2-36之间的,其他都为NaN,parseInt(10,2)为 1* 2^ 1 + 0 * 2^ 0 = 2;
    所以结果返回[NaN,NaN,3];

JS-V8垃圾回收

什么是V8?

V8是一个开源的javascript引擎,基于C++编写而成。谷歌浏览器就是基于V8引擎进行搭建的。同时node也使用了V8。

V8的新老空间内存分配与大小限制

新老空间

凡事都有一把双刃剑,在垃圾回收的演变过程中人们发现,没有一种特定的垃圾回收机制是可以完美的解决问题,因此V8采用了新生代与老生代结合的垃圾回收方式,将内存分为新生代和老生代。
新生代频繁进行GC,空间小,采用的是空间换时间的scavenge算法,所以又划分为两块semispace,From和To。
老生代大部分保存的是存活时间较长的或者较大的对象。采用的是mark-sweep(主)&mark-compact(辅)算法。

新生代与老生代.png

V8限制了js对象可以使用的内存空间,不止是因为最初V8是作为浏览器引擎而设计的。还有其垃圾回收机制的影响因素。V8使用stop-the-world(全停顿), generational, accurate的垃圾回收器。在执行回收之时会暂时中断程序的执行,而且只处理对象堆栈。当内存达到一定的体积时,进行一次垃圾回收的时间将会很长,从而影响其相应而造成浏览器假死的状况。因此,在V8中限制老生代64位为1.4GB,32位为0.7GB,新生代64位为32M,32位为16M。
当然,如果需要更大的内存空间,在node中可以进行更改。

对象晋升

新生成的对象放入新生代内存中,那哪些对象会被放入老生代中呢?大部分放入老生代的对象是由新生代晋升而来。对象的晋升的方式:

  1. 当新生代的To semispace内存占满25%时,此时再从From semispace拷贝对象将不会再放入To空间中以防影响后续的新对象分配,而将其直接复制到老生代空间中。

  2. 在进行一次垃圾回收后,第二次GC时,发现已经经历过一次GC的对象在从From空间复制时直接复制到老生代。

  3. 在新对象分配时大部分对象被分配到新生代的From semispace,但当这个对象的体积过大,超过1MB的内存页时,直接分配到老生代中的large Object Space。

新生代的GC机制与优缺点

回收机制

新生代采用Scavenge算法,在scavenge算法的实现过程中,则主要采用了cheney算法。即使用复制方式来实现垃圾回收。它将内存一分为二,每一个空间都是一个semispace。

处于使用状态的是From空间,闲置的是To空间。当分配对象时,先是分配到From空间,垃圾回收时会检查From空间中存活的对象,将其复制到To空间,回收其他的对象。完成复制后会进行紧缩,From和To空间的调换。如此循环往复。

优势

由其执行的算法及过程我们可以了解到,在新生代的垃圾回收过程中,总是由一半的semispace是空余的。scavenge只复制存活的对象,在新生代的内存中,存活的对象相对较少,所以使用这个算法恰到好处。

老生代的GC机制与优缺点

回收机制

由于的scavenge算法只复制存活的对象,如果在老生代中也使用此算法的话就会造成复制很多对象,效率低,并且造成很大的内存空间浪费。
老生代中采用的则是mark-sweep(标记清除)和mark-compact(标记整理)结合的方式。而为什么使用两者结合呢?这就要讲到两者的优点与缺点。

mark-sweep(标记清除)

  1. 优点

    1.1 标记清除需要标记堆内存中的所有对象,标记出在使用的对象,清除那些没有被标记的对象。在老生代内存中与新生代相反,不使用的对象只占很小一部分,所以清除不用的对象效率高。

    1.2.mark-sweep不会将内存空间分为两半,所以,不会浪费一半空间。

  2. 缺点

    但标记清除会造成一个问题,就是在清除过后会导致内存不连续,造成内存碎片,如果此时需要储存一个很大的内存而空间又不够的时候就会造成没有必要的反复垃圾回收。
    内存中的对象.png
    标记过程.png
    标记清除过程.png

mark-compact(标记整理)

  1. 优点

    此时标记整理就可以出场了,在标记清除的过程中,标记整理会将存活的对象和需要清除的对象移动到两端。然后将其中一段需要清除的消灭掉,可以解决标记清除造成的内存碎片问题。

  2. 缺点

    但是在紧缩内存的过程中需要移动对象,效率比较低。所以V8在清理时主要会使用Mark-sweep,在空间不足以对新生代中晋升过来的对象进行分配时才会使用Mark-compact。
    标记整理.png

垃圾回收机制的优化

增量标记(在老空间里引入了此方式)

scavenge算法,mark-sweep及mark-compact都会导致stop-the-world(全停顿)。而全停顿很容易带来明显的程序迟滞,标记阶段很容易就会超过100ms,因此V8引入了增量标记,将标记阶段分为若干小步骤,每个步骤控制在5ms内,每运行一段时间标记动作,就让JavaScript程序执行一会儿,如此交替,明显地提高了程序流畅性,一定程度上避免了长时间卡顿。

JS - BOM

  • BOM提供了很多对象,用于访问浏览器的功能

  • W3C为了BOM的标准化,将BOM的主要方面纳入了HTML5的规范中

  • BOM对象

    • window

      • 全局变量不能通过delete删除,但是定义在window上的属性可以
      var age = 20;
      window.test = 'test';
      delete window.age //false,因为configurable 为false
      delete window.test //true
      
      • 窗口位置
        • screenTop screenLeft /chrome
        • screenX screenY/FireFox
        • window.moveTo(200,300)//移动到
        • window.moveBy(0,100)//向下移动100
      • 窗口大小
        • innerWidth
        • innerHeight
        • outerWidth
        • outerHeight
      • window.open
        • 四个参数
          • 要加载的url
          • 窗口目标
          • 一个特性字符串
          • 新页面是否取代浏览器历史记录中当前加载页面的布尔值
        • 举例
        window.open("http://www.baidu.com","","width=400,height=400")
        //新打开一个窗口,大小为400*400,在firefox下可以,chorme下不可以
        
      • 系统对话框
        • 弹出对话框
          • alert
          • confirm
          • prompt
          • 特点
            • 不包含HTML
            • 样式不是由CSS决定
        • window.print() 打印
          • 异步的
    • location

      • 作用
        • 提供当前窗口中加载的文档的相关信息
        • 提供一些导航功能
      • 特点
        • 即是window的属性
        • 也是document的属性
      • 属性
      • 位置操作
        • location.href/window.location
          • 导航到新的位置,并在历史记录中新加入一条
          • 本质是调用location.assign()
        • location.hash
          • 以上location的属性,修改的时候都在历史记录中生成一个新记录,因此用户单击后退会导航到前一个页面
          • location.replace()不会产生新纪录,后退按钮处于禁用状态
          • location.reload
            • location.reload()//有可能从浏览器缓存中加载
            • location.reload(true)//从服务器加载
    • navigtion

    • history

      • 保存用户上网的历史记录
      • 是window的属性
        • 每一个窗口都有自己的history
        • histoty.go(-1)
    • screen

JS-装箱&拆箱转换

  • 前提:
    js中的基本类型String,Number,Boolean,Symbol有对应的基本包装对象

  • 装箱
    将基本类型转换为基本包装对象

var s1 = "some text";
var s2 = s1.substring(2);

s1是一个基本类型string,本身是没有方法的,却可以使用String上面的方法

  • 使用的方法,就是

    var str = 'test'
    var strC = new String(str);
    
    strRes = str.split('');
    strC = null;
    
  • 实际步骤就是

    • 创建一个包装对象的实例
    • 调用实例上的方法
    • 销毁实例
  • 拓展:有人说装箱操作会泄漏内存,但是最后进行了实例的销毁,那究竟是怎样泄漏的内存呢


  • 拆箱

将基本包装对象转换为基本类型,拆箱主要有两个方法来检测你返回的是否是一个基础类型

  • valueOf

  • toString

        var objNum = new Number(123);  
        var objStr =new String("123");  
        console.log( typeof objNum ); //object
        console.log( typeof objStr ); //object
        console.log( typeof objNum.valueOf() ); //number
        console.log( typeof objStr.valueOf() ); //string
        console.log( typeof objNum.toString() ); // string 
        console.log( typeof objStr.toString() ); // string
    
  • 拆箱例子

[]+[]	//""
{}+{}	//"[object Object][object Object]"
[]+{}	//"[object Object]"
{}+[]	//0 这里发生了隐式转换“”=>0

以上都是先调用的valueOf,然后调用toString
[].valueOf().toString() //""空字符串
({}).valueOf().toString()//

var a = {
    valueOf:function () {
        console.log(this);
        console.log('valueOf');
        return {name:'valueOf'}
    },
    toString:function () {
        console.log(this);
        console.log('toString');
        return {name:'toString'}
    }
}
a*2;
//可以看到先执行了valueOf,而后执行了toString
alert(a);
//可以看到先执行了toString,而后执行了valueOf
  • valueOf和toString的执行顺序不是固定的
  • 那么是什么决定了两个方法的决定顺序呢
    • ToPrimitive,拆箱转换的实现函数
    • 遵循先拆箱后转换的规则(String,Number)
      • 调用顺序
        • 检查对象中是否有自定义的[Symbol.toPrimitive]方法,有则直接调用
        • 没有,执行原内部函数的toPrimitive
          • 然后判断传入的hint值
          • 其值为string,则先调用toString,如果其返回一个基本类型值,则终止运算,否则继续调用valueOf
          • 其值为非string,即number或者default,则先调用valueOf,如果其返回一个基本类型值,则终止运算,否则继续调用toString
            • hint值
              • string
                • 对象=》string
                • 如alert(a)
              • number
                • 对象=》number
              • default
                • 对象=》不确定类型

JS事件循环-宏任务&微任务

JS的灵魂特征

js语言的最重要的特点:单线程的非阻塞IO

为什么是单线程的,多线程不是更快吗。

原因:

js在最初是为浏览器设计的语言,所以会操作DOM,那假设是多线程的,一个线程修改DOM元素,一个线程删除DOM元素,
那浏览器应该删除还是修改?

缺点:

但是单线程注定会很慢。造成了多核CPU的浪费。

解决:

HTML5推出了Web Worker,允许js创建多个线程,但是每个子线程完全受主线程的控制。且不得操作DOM。

由此引出的问题:
web

  • worker的优点缺点?
  • 如何使用?
  • 原理是什么

第二部分:任务队列

  • 单线程时,任务要排队列执行。

任务又分为:

  • 同步队列(在主线程上排队的任务,形成执行栈execution context stack,必须前一个先执行完成,才能进行下一个任务)

  • 异步队列(不进入主线程,而是在任务队列task queue上面,任务队列通知主线程,某异步任务可以执行了,才会进入主线程。异步任务分为两类:

    • 微任务 Promise async/await
    • 宏任务 setTimeout setInterval
      执行栈在执行所有同步任务之后,会检查微任务队列是否为空,不为空,则取微任务到执行栈,执行所有微任务,然后执行宏任务。每个宏任务执行过后都会去检查微任务队列,如此循环,也就是说微任务一直在宏任务前面。

执行栈中所有的任务都执行完成了,就会读取任务队列,可以执行的异步任务,进入执行栈。
重复操作第3步
这个过程是个循环,因此得名Event Loop。

JS-instanceof 实现

a instanceof b:是在原型链上查找是否有原型= b.prototype

function myInstanceof(left,right) {
  left = Object.getPrototypeOf(left);//或者使用__proto__ 
  right = right.prptotype;
  while(true){
      if(left === null) return false
      if(left === right) return true
      left =  Object.getPrototypeOf(left);
  }
}

JS-七种基本类型之Object

概念:对象的定义
在英文中,对象是指一切物体的集合。生活中,车是一种对象,电动汽车是其中的一个类。

js中的对象:
同样,js中万物皆对象,是属性与值的集合。

分类:

  • 内置对象(js本身提供的对象)
    • 固有对象

    • 原生对象:能够通过语言本身的构造器创建的对象

      • 基本类型
        • Number
        • String
        • Boolean
        • Symbol
        • Object
      • 基础功能和数据结构
        • Array
        • Date
        • Promise
        • Proxy
        • Map
        • Set
        • Function
      • 错误类型等其他共30多种
    • 普通对象:由语法{},function,Object构造器,或者class关键字创建出来的对象,可以被原型继承

创建普通对象的几种方法
var a = {};
var b = function (){}
var c = Object.create(null)
class Dog{
    constrouctor(){
        
    }
}
  • 宿主对象(js所处的宿主环境提供的对象,前端中我们通常指浏览器环境)
    浏览器环境中全局对象是window,window上有很多属性,这些属性就是由js内置对象和宿主对象提供的。

内置对象和宿主对象是对象分类的一方面,另外还可以从模拟函数和构造器来区分对象。

函数对象:具有私有字段call的对象;

构造器对象:具有私有字段construct的对象;

  • 基本类型(String、Number、Boolean),它们的构造器被当作函数调用,则产生类型转换的效果

  • 通过function关键字创建的函数同时是函数和构造器。

  • ES6 之后 => 语法(箭头函数)创建的函数仅仅是函数,它们无法被当作构造器使用,见以下代码:

new (a => 0) // error
  • function 语法或者 Function 构造器创建的对象来说,[[call]] 和 [[construct]] 行为总是相似的。
function f(){
    return 1;
}
var v = f(); // 把 f 作为函数调用
var o = new f(); // 把 f 作为构造器调用
  • 还有一些其他的对象很特殊
    • Array:Array 的 length 属性根据最大的下标自动发生变化。
    • Object.prototype:作为所有正常对象的默认原型,不能再给它设置原型了。String:为了支持下标运算,String 的正整数属性访问会去字符串里查找
    • 。Arguments:arguments 的非负整数型下标属性跟对应的变量联动。
    • 模块的 namespace 对象:特殊的地方非常多,跟一般对象完全不一样,尽量只用于 import 吧。
    • 类型数组和数组缓冲区:跟内存块相关联,下标运算比较特殊。
    • bind 后的 function:跟原来的函数相关联。

常用的对象方法

  • Object.assign
  • Object.create
  • Object.defineProperty
  • Object.keys
  • Object.values

JS-深浅克隆

  • 浅拷贝
    • 当有对象,数组等数据时,只拷贝值的引用地址,所以会导致一个变,拷贝的值也变
function clone(obj) {
    return newObj = Object.assign(obj);
}
//验证
var a = {
    a1:1,
    a2:[2,3]
}
var b = clone(a);
b.a1 = 'test';
console.log(a)
console.log(b)

//深拷贝1--JSON.stringify

function clone(obj){
    return JSON.parse(JSON.stringify(obj))
}
var a = {
    a1:1,
    a2:[2,3]
}
var b = clone(a);
b.a1 = 'test';
console.log(a)
console.log(b)

//深拷贝2- 递归

function clone(obj) {
    let target;
    if(typeof obj !== 'object'){
        return obj;
    }else {
        target = Array.isArray(obj) ? [] : {};
        for(let k in obj) {
            if(typeof obj[k] === 'obj'){
                target[k] = clone(obj[k])
            }else {
                 target[k] = obj[k]
            }
        }
    }
    return target;
}

//深拷贝3 -- 二分法

function cloneLoop(x) {
    const root = {};

    // 栈
    const loopList = [
        {
            parent: root,
            key: undefined,
            data: x,
        }
    ];

    while(loopList.length) {
        // 深度优先
        const node = loopList.pop();
        const parent = node.parent;
        const key = node.key;
        const data = node.data;

        // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
        let res = parent;
        if (typeof key !== 'undefined') {
            res = parent[key] = {};
        }

        for(let k in data) {
            if (data.hasOwnProperty(k)) {
                if (typeof data[k] === 'object') {
                    // 下一次循环
                    loopList.push({
                        parent: res,
                        key: k,
                        data: data[k],
                    });
                } else {
                    res[k] = data[k];
                }
            }
        }
    }

    return root;
}

JS-深拷贝

拷贝数组的时候

可以利用数组的一些方法

  1. slice
  2. concat
  3. es6的...解构赋值

拷贝深层次对象或者数组的时候

  1. JSON.parse&JSON.stringify
let obj ={
  a:1,
  b:{
    b1:12
  }
}
let objClone = JSON.parse(JSON.stringify(obj));
objClone.a = 'test';
objClone.b.b1 = 23333;
console.log(obj);

问题:无法拷贝函数

  1. 暴力循环
function deepClone (obj) {
  if(typeof obj !== 'object' ) return;
  var newObj = obj instanceof Array ? [] :{};
  for(let key in obj) {
    if(obj.hasOwnProperty(key)){
      newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
    }
  }
  return newObj;
}

JS- 实现bind

知己知彼 百战百胜

我们需要先知道bind都做了些什么,有哪些特性

  1. 改变this指向参数1
  2. 返回一个携带上下文的函数
  3. 接受柯里化传参
  4. 可以使用new 调用
Function.prototype.myBind = function (context){
  var _self = this;//缓存this
  var args = Array.prototype.slice.call(arguments, 1); //缓存参数
  var bindFun = function () {
    var newArgs = Array.prototype.slice.call(arguments);
    return _self.apply( this instanceof bindFun ? this : context, args.concat(newArgs)) 
  }
  bindFun.prototype = this.prototype;
  return bindFun;
}
var value = 2;

var foo = {
    value: 1
};

function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}

bar.prototype.friend = 'kevin';

var bindFoo = bar.myBind(foo, 'daisy');

var obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);

JS-七种基本类型之 Array

  • 数组遍历方法
  1. 循环遍历
let arr = [2,4,7,9];
for(let i = 0; i< arr.length; i++) {
    console.log(arr[i]);
}
  1. for in(es5)
let arr = [2,4,7,9];
for(var i in arr) {
    console.log(arr[i]);
}
  1. for of(es6)
let arr = [2,4,7,9];
for(var i of arr) {
    console.log(i);
}
  1. forEach(es6)
let arr = [2,4,7,9];
arr.forEach((item)=> {
  console.log(item)
})
  1. map(es6)
let arr = [2,4,7,9];
arr.forEach((item)=> {
  console.log(item)
})

JS-闭包、执行上下文&作用域,都是什么鬼

执行上下文

定义:JS的一段代码(包括函数)执行所需的所有信息。

所有信息不仅包括词法环境,还包括this指向,变量声明,作用域链,with等复杂语法。

从实践出发来看一下

var b = {};
let c = 1;
this.a = 2;
  1. var 把b 声明到哪里
  2. b表示哪个变量
  3. b的原型是谁
  4. let把c声明到哪里
  5. this指向谁

这些就是表示执行上下文。


var 声明作用域函数执行的作用域,也就是说var 会穿透for、if语句。
所以在没有let的时候,我们通常使用创建立即执行函数来创建一个作用域,从而控制var声明的范围。

声明立即执行函数的方式
1.外层括号方式、
(function () {
    var a = 1;
}());
(function () {
    var a = 1;
})();

2.void 方式
void function () {
    var b = 2;
}();

js中括号的问题
括号的缺点,js中,如果上一行代码不写分号,括号会被解释为上一行代码最末的函数的调用。
而且function关键字开头是函数声明。


JS-使用Promise实现串联

function makePromise (value) {
  return new Promise((resolve) => {
    setTimeout(() => {
      // console.log(value,'====')
      resolve(value);
    }, value * 1000)
  })
}
/**
 * 打印结果
 * @param {Number} value 
 */
function print (value) {
  console.log(value,'value====')
  return value
}

let promises = [1, 1, 2, 6, 3].map((item, index) => {
  return makePromise(item)
});

// // 并行执行
// Promise.all(promises)
// .then(() => {
//   console.log('done')
// })
// .catch(() => {
//   console.log('error')
// })
// 串行执行
let parallelPromises = promises.reduce(
  (total, currentValue) => total.then(() => currentValue.then(print)),Promise.resolve()
)

parallelPromises
.then(() => {
  console.log('done-----')
})
.catch(() => {
  console.log('done')
})

JS-为什么0.1+ 0.2 不等于0.3

  • 首先0.1+ 0.2 不等于0.3指的是在JS中,

console.log(0.1+0.2 == 0.3)//结果为false

  • 为什么如此,与JS中的Number类型有关。JS的Number类型基本符合双精度浮点数规则;
    https://www.cnblogs.com/xiaohuochai/p/5586166.html
    根据国际标准IEEE 754,javascript浮点数的64个二进制位
    第1位: 符号位,0表示正数,1表示负数
    第2位到第12位: 储存指数部分
    第13位到第64位:储存小数部分(即有效数字)

根据浮点数的定义,非整数无法使用双等号(==)或者全等号(===)来进行比较;

  • 正确的比较方法是检查等号左右两边绝对值的差是否小于最小精度;

console.log(Math.abs(0.1+0.2 - 0.3) <= Number.EPSILON) //true

JS-javascript规定了几种语言类型

  • 7种
  1. string
  2. number
  3. boolean
  4. null
  5. undefined
  6. object
  7. symbol

所以其实我们也可以看出,在js中有一句很耳熟能详的‘万物皆对象’这句话有一定的不准确性
因为object只是7种基本类型中的一种,当然对象又很多特殊子类型,比如

  1. Array
  2. String
  3. Number
  4. Boolean
  5. Object
  6. Function
  7. expReg
  8. Error
  9. Symbol

并且在typeof null === object //true
因为在js的底层设计中以二进制表示基本类型,开头前3位为0就被识别为object,而null全部都是0

  • 当然还可以按照另一种分类,分为值访问类型和引用类型
    • 值访问类型

      1. string
      2. number
      3. boolean
      4. null
      5. undefined
      6. symbol
    • 引用类型

      1. object

按照值储存在栈里
引用类型的值指针储存在栈中,访问值的时候,先从栈中获得对象的地址指针,然后通过指针找到储存在堆中的数据。

拓展
Javascript中String()和new String()的区别
String(1) 是值访问类型,也就是基本类型string
new String(1)返回的是引用类型,也就是一个对象String{0:1}
所以String(1) === new String(1)//false
String(1) === new String(1)[0]//true
String(1)== new String(1)//true

JS - 闭包

  • 关于闭包的定义?

    • 就像一千个读者就有一千个哈姆雷特一样,闭包的定义也是纷繁多样
    • js高级程序设计中这样定义
      • 指有权访问另一个函数作用域中的变量 的函数
    • 你不知道的js中这样定义
      • 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域外执行
    • MDN
      • 能够访问自由变量的函数
      • 自由变量
        • 在函数中使用的,但既不是函数参数也不是函数局部变量的变量
  • 分析

    function create(prototypeName) {
        return function (object1,object2) {
            var val1 = object1[prototypeName];
            var val2 = object2[prototypeName];
            if(val1) {
                return val1
            }else {
                return val2
            }
        }
    }
    
    
    • val1,val2都是内部函数中代码
    • 访问外部函数中的变量prototypeName
    • 即使内部函数被返回了,而且在其他地方被调用了,但它仍然可以访问prototypeName
    • 原因是内部函数的作用域链中包含了create()的作用域
    • 函数内部定义的函数会把函数(外部函数)的活动对象添加到它的作用链中
    • 外部函数执行后,活动对象不会被销毁,因为闭包函数的作用域链仍然在引用这个活动对象
    • 因此闭包比正常的函数占用更多的内存
    • 可以使用 = null的方式清除
  • 什么是作用域链

    • 当函数第一次被调用的时候,会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的属性[[Scope]]
    • 然后,使用this、arguments和其他命名参数的值来初始化函数的活动对象-AO(activation object)
    • 作用域链本质上是一个指向变量对象的指针列表
    • 函数执行之后,局部后动对象会被销毁,内存中只保存全局执行环境的变量对象-VO(Global Variable Object)
  • 闭包分析

    var data = [];
    
    for (var i = 0; i < 3; i++) {
      data[i] = function () {
        console.log(i);
      };
    }
    
    data[0]();
    data[1]();
    data[2]();
    
    • 当执行到 data[0] 函数之前,此时全局上下文的 VO 为:
    globalContext = {
        VO: {
            data: [...],
            i: 3
        }
    }
    
    • 当执行 data[0] 函数的时候,data[0] 函数的作用域链为:
    data[0]Context = {
        Scope: [AO, globalContext.VO]
    }
    
    • data[0]Context 的 AO 并没有 i 值,所以会从 globalContext.VO 中查找,i 为 3,所以打印的结果就是 3。
    • data[1] 和 data[2] 是一样的道理。所以让我们改成闭包看看:
    var data = [];
    
    for (var i = 0; i < 3; i++) {
      data[i] = (function (i) {
            return function(){
                console.log(i);
            }
      })(i);
    }
    
    data[0]();
    data[1]();
    data[2]();
    
    • 当执行到 data[0] 函数之前,此时全局上下文的 VO 为:
    globalContext = {
        VO: {
            data: [...],
            i: 3
        }
    }
    
    • 跟没改之前一模一样。
    • 当执行 data[0] 函数的时候,data[0] 函数的作用域链发生了改变:
    data[0]Context = {
        Scope: [AO, 匿名函数Context.AO globalContext.VO]
    }
    
    • 匿名函数执行上下文的AO为:
    匿名函数Context = {
    AO: {
        arguments: {
                0: 0,
                length: 1
            },
            i: 0
        }
    }
    
    • data[0]Context 的 AO 并没有 i 值,所以会沿着作用域链从匿名函数 Context.AO 中查找,这时候就会找 i 为 0,找到了就不会往 globalContext.VO 中查找了,即使 globalContext.VO 也有 i 的值(值为3),所以打印的结果就是0。

    • data[1] 和 data[2] 是一样的道理。

JS-七种基本类型之String

基本概念

  • 生成字符串类型
  1. 字符串字面量
var str = 'fghjkl';
console.log(typeof str)//string
  1. 调用String方法
var str_string = String('fghjkl');
console.log(typeof str_string)//string

注意

当使用new String时创建的就不是字符串类型,而是对象类型;
因为string在object中有一个亲戚String()(Number,Boolean相同)
基本字符串,就是type of为string的字符串,可以使用String()(字符串对象)的方法是因为
.(点运算符)进行了装箱操作


属性(两个)

  1. length 长度
const str = 'rtyu';
console.log(str.length)//4
  1. prototype 原型

方法(很多,大概49个,这里只例举常用的一些)

es5

  1. 获取单个字符
  • charAt
    语法:str.charAt(index)//index范围(0-str.length -1),默认为0,index超过str.length -1返回空字符串
var str = 'rtyuio';
console.log(str.charAt(1))//'t'
  • 把字符串当作一个类似数组的对象,每个字符对应下标
var str = 'rtyuio';
console.log(str[2])//'y'
  1. 连接两个字符串
  • concat(同array):返回的是新字符串
var str_a = 'test';
var str_b = 'stringConcat';
console.log(str_a.concat(str_b))//'teststringConcat'
  1. 大小写转换
  • toLowerCase/toUpperCase
var str_lower = 'rtyyu';
console.log(str_lower.toUpperCase())//'RTYYU'
  1. 截取字符串类
  • slice:截取字符串的某部分,返回一个新的字符串
    语法:str.slice(beginIndex,endIndex:不包含最后一位)

beginIndex:0-str.length;如果为负数,则为str.length - beginIndex
endIndex::0-str.length;如果为负数,则为str.length - beginIndex
endIndex>=beginIndex的情况时返回空字符串

var str_slice = 'rtyuio';
console.log(str_slice.slice(3,5))//ui
console.log(str_slice.slice(-3,5))//ui
console.log(str_slice.slice(3,-5))//''
console.log(str_slice.slice(-2,5))//i
  • split:以一个分隔符将字符串分割为数组
    语法:split(separator, limit)
    separator:可以是字符串或者正则表达式
    limit:整数,0则返回空数组,负数则忽略
var myString = "Hello World. How are you doing?";
var splits = myString.split(" ", 3);
console.log(splits);//['Hello ','World. ','How ']
splits = myString.split(" ", 0);
console.log(splits);//[]
  • substring:返回一个开始索引到结束索引的子集(新字符串)
    语法:str.substring(indexStart, indexEnd)
    indexStart:0-str.length;
    indexEnd:0-str.length;

indexStart < 0 || indexEnd < 0 ,默认为0
indexEnd > str.length || indexStart > str.length,默认为str.length
indexStart > indexEnd,则调换两者位置
indexStart = indexEnd,则返回空字符串

var anyString = "Mozilla";
// 输出 "Moz"
console.log(anyString.substring(0,3));
console.log(anyString.substring(3,0));
console.log(anyString.substring(3,-3));

JS - 实现add(1)(2)(3,4)(5) --柯里化

function add () {
   return [...arguments].reduce((a, b) => {
    return a + b;
  })
}
function curry (fn) {
  var args = []
   return function _c (...newArgs){
    if(newArgs.length){
        args = [...args, ...newArgs]
        return _c;
     }else {
        return fn.apply(this, args)
    }
  }
}
let addCurry = currying(add)
let test = addCurry(1)(2)(3)(4,5)();
console.log(test,'test==')

JS - 实现call&apply

  • call
    • 绑定指定的this
    • 可以传入多个参数
    • 执行函数
Function.prototype.myCall = function (context, ...args) {
  var context = context || window;
  context.fn = this;
  let res = context.fn(...args);
  return res;
}
//验证
var obj = {
  name: 'test my call'
}
var test = function (age) {
  console.log(this.name,'🍎')
  console.log(age,'🍌')
  return '我是test'
}
test.myCall(obj,18);

//apply与call唯一的不通之处在于传参形式,为数组
Function.prototype.myApply = function (context, args) {
  var context = context || window;
  context.fn = this;
  if(!args || args.length < 0) {
    args = [undefined]
  }
  let res = context.fn(...args);
  return res;
}

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.