资讯专栏INFORMATION COLUMN

【芝士整理】JS基础图谱

netScorpion / 2352人阅读

摘要:没有找到的话,看上级函数作用域,向上查找到,找到为止。将会在执行上下文栈中保留上级作用域的执行上下文。若在闭包使用完毕之后不手动解除引用,相关执行上下文将会一直保留于执行上下文栈中,占据内存空间,若持续积累,容易造成内存泄漏。

JS有哪些基本数据类型呢?

值类型:undefined, Number, Boolean, String,null

引用类型:Object

值类型存放在栈中

引用类型将地址存放在栈中,将数据实体存放在堆中

null和undefined,not defined的区别?

not defined是未声明,当使用未声明变量时浏览器会抛出这个错误

undefined是已声明未赋值,typeof undefined是undefined

null类似于空对象,是一个已定义,定义为空的值,typeof null 是 object

如何判断数据类型?

如果是值类型,直接用typeof判断

如果是引用类型,使用instanceof判断,instanceof基于原型链,一般用于判断自定义对象

constructor是prototype上的一个属性,他容易被重写覆盖,所以不可信赖

Object.prototype.toString.call,调用Object原型上的toString方法可以得到当前调用者的具体类型

Object.prototype.toString.call().slice(8, -1); // Object|Array|Number|String|Boolean...
什么是原型链?

每一个函数上都有一个prototype属性,称为原型对象

函数实例化产生对象

每一个对象都有一个__proto__(隐匿原型)属性,指向构造它的原型对象。

原型对象本身也是对象,也有一个隐匿原型,指向它的原型对象。

沿着隐匿原型链最终会指向Object.prototype,它的原型对象是null

这就构成一个原型链

PS. 将原子类型赋给 prototype 的操作将会被忽略

function Foo() {}
Foo.prototype = 1; // 无效

instanceof的原理

A instanceof B
A的原型链是否会到达B.prototype

继承

通过原型链实现继承,原型对象上可以定义属性和方法。

当要在一个对象上寻找某个属性,先在对象本身找,没有的话,再沿着原型链向上找原型对象里有没有,向上查找找到为止,到达顶部仍未找到,返回undefined

PS.判断对象上是否有某个属性,而非其原型链上有,使用hasOwnProperty 函数
执行上下文

在函数调用时或者是全局代码开始运行时产生,处理的事情:变量声明,函数声明,函数声明形式的定义赋值,定义this,在函数内还有定义arguments的操作

PS. arguments 变量不是一个数组(Array)。 尽管在语法上它有数组相关的属性 length,但它不从 Array.prototype 继承,实际上它是一个对象(Object)。

因此,无法对 arguments 变量使用标准的数组方法,比如 push, pop 或者 slice。 虽然使用 for 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。

Array.prototype.slice.call(arguments);
执行上下文栈

全局代码开始执行时,产生一个全局的执行上下文,压栈

代码执行到函数A调用时,产生一个函数A的执行上下文,压栈

函数A中调用函数B,产生一个函数B的执行上下文,压栈

函数B,执行完毕,出栈销毁执行上下文

函数A,执行完毕,出栈并销毁执行上下文

关于this的指向

this存在于执行上下文中

作为构造函数调用时,指向实例化的对象

作为对象属性调用时,指向对象

call, apply调用时,指向参数指定上下文

全局和普通函数都指向windows

ES6中,箭头函数本身没有this,导致以下三种现象:根据外层(函数或者全局)作用域来决定this,箭头函数不能作为构造函数使用,不能使用call, apply手动修改this

PS. 一些误解

// 1. 严格按照规范
Foo.method = function() {
    // 在这,this是Foo的实例化对象
    function test() {
        // this 将会被设置为全局对象
    }
    test();
}

// 2. 函数别名
var test = someObject.methodTest;
test(); // this设置为全局对象
PS. apply和call的用法

function.apply(null, arguments);

function.call(null, arg1, arg2);

作用域

ES5中只有函数作用域的概念,作用域是一个虚拟概念,没有具体的数据类型或者结构。

一个函数的作用域在函数定义时确定,创建函数的作用域成为该函数的上级作用域

在函数中寻找变量,先找到函数作用域对应的执行上下文,在执行上下文中找变量。

没有找到的话,看上级函数作用域,向上查找到,找到为止。

如果找不到,则会抛出 ReferenceError异常。

PS. 比如,当访问函数内的 foo 变量时,JavaScript 会按照下面顺序查找:

当前作用域内是否有 var foo 的定义。

函数形式参数是否有使用 foo 名称的。

函数自身是否叫做 foo

回溯到上一级作用域,然后从 #1 重新开始。

闭包

什么是闭包?
一个函数中有依赖外部变量,函数在创建它的作用域之外被调用。
将会在执行上下文栈中保留上级作用域的执行上下文。
若在闭包使用完毕之后不手动解除引用,相关执行上下文将会一直保留于执行上下文栈中,占据内存空间,若持续积累,容易造成内存泄漏。

常见应用,函数作为返回值,函数作为参数。

经典问题

for (var i = 0; i < 5; i++) {
    setTimeout(function() {  
      console.log(i);
      }, 1000);
} // 5,5,5,5,5

for (let i = 0; i < 5; i++) {
    setTimeout(function() {  
      console.log(i);
      }, 1000);
} // 0,1,2,3,4

for (var i = 0; i < 5; i++) {
    (function (i) {
        setTimeout(function() {  
          console.log(i);
          }, 1000);
    })(i)
} // 0, 1, 2, 3, 4

for(var i = 0; i < 5; i++) {
    setTimeout((function(e) {
        return function() {
            console.log(e);
        }
    })(i), 1000)
} // 0, 1, 2, 3, 4
JS中实现继承的方法

基础继承

var Bar = function () {};
Bar.prototype = {
    greet: function (name) {
        console.log(name);
    }
}
var Foo = function () {}
Foo.prototype.__proto__ = Bar.prototype;

等价于

var Bar = function () {};
var Foo = function () {}
Foo.prototype = new Bar();

原理:实现原型链

缺点:属性不独立

组合继承

var Bar = function (name) {
    this.name = name;
}
Bar.prototype = {
    greet: function () {
        console.log(this.name);
    }
}
var Foo = function (name) {
    Bar.apply(this, arguments);
}
Foo.prototype = new Bar();

原理:把this属性赋值在子类的作用域执行一次,方法通过原型链继承
缺点:this属性赋值进行了两次

寄生组合式继承

var Bar = function (name) {
    this.name = name;
}
Bar.prototype = {
    greet: function () {
        console.log(this.name);
    }
}
var Foo = function (name) {
    Bar.apply(this, arguments);
}
Foo.prototype = Object.create(Bar.prototype);
Foo.prototype.constructor = Foo;

原理: 把this属性赋值在子类的作用域执行一次,手动连接原型对象的拷贝
优点:解决组合继承的缺点

extends方法

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    
    toString () {
        return this.x + " " + this.y;
    }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + " " + super.toString(); // 调用父类的toString()
  }
}

子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

PS. new 运算符做了什么

// 1. 首先创建一个空对象
var o = new Object();
// 2. 将空对象的原型赋值为构造器函数的原型
o.__proto__ = A.prototype;
// 3. 更改构造器函数内部this,将其指向新创建的空对象
A.call(o);
类型转换 强制转换

转为字符串::.toString(), String()

转为数值:Number针对所有类型,parseInt和parseFloat针对字符串

字符串转换为数字的常用方法:

+"010" === 10
Number("010") === 10
parseInt("010", 10) === 10  // 用来转换为整数

+"010.2" === 10.2
Number("010.2") === 10.2
parseInt("010.2", 10) === 10
parseFloat("10.1.2") === 10.1 // 字符转换为浮点数

Number(undefined) // NaN
Number严格转换,只要有一个字符无法转为数值输出NaN
parseInt原理为从左往右读字符串,读到非数值字符为止
parseFloat原理为从左往右读字符串,读到第二个小数点或者非数值非小数点字符为止

转为布尔值:Boolean(),""、null、undefined、+0、-0 和 NaN 转为布尔型是 false,其他的都是 true

自动转换

转为字符串:包含字符串的+法运算,"5" + 1 === "51"

转为数值:不包含字符串的算术运算

转为布尔值:条件语句判断,非运算

事件轮询机制

主线程运行时产生堆和执行栈

主线程之外,还存在一个"任务队列"。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。对应的异步任务,结束等待状态,进入执行栈,开始执行

任务队列

任务队列分为宏任务队列和微任务队列

宏任务包括:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering。

微任务包括: new Promise().then(回调), process.nextTick, Object.observe(已废弃), MutationObserver(html5新特性)

执行同步任务 -> 处理微任务队列 -> 处理宏任务队列里队首任务 -> 处理微任务队列

Promise.resolve().then(()=>{
  console.log("Promise1")  
  setTimeout(()=>{
    console.log("setTimeout1")
  },0)
})
setTimeout(()=>{
  console.log("setTimeout2")
  Promise.resolve().then(()=>{
    console.log("Promise2")    
  })
  Promise.resolve().then(()=>{
  console.log("Promise3")    
  })
},0)
setTimeout(()=>{
  console.log("setTimeout4")
  Promise.resolve().then(()=>{
    console.log("Promise4")    
  })
},0)

Output: Promise1 setTimout2 Promise2 Promise3 setTimeout4 Promise4 setTimeout1
setTimeout和setInterval的区别

setTimeout:产生一个宏任务,在指定时间之后加入任务队列。

setInterval:循环产生宏任务,但存在问题,若任务执行时间长于指定时间间隔,会产生堆叠执行效果。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100410.html

相关文章

  • 芝士整理】CSS基础图谱

    摘要:为了实现文字环绕效果,规范规定的措施是使父容器塌陷,元素脱离文档流浮动产生,元素周围的内容转换为围绕元素排列。 选择器注意点 属性选择器 [attr^=value] - 开头或全等 [attr$=value] - 结尾或全等 [attr*=value] - 包含值 [attr~=value] - 字符串包含 选择器组 A > B - 直接子节点 A + B - 下一个兄弟节点 A...

    iOS122 评论0 收藏0
  • 芝士整理】HTML的标签们

    摘要:读一遍文档后的个人总结,重点在于整理语义化标签的定义规范,记录各种部件容易被忽略的特性。结构化,通过标签先后顺序和嵌套语法给树提供基础。标签列表基于个人理解即非官方描述,给标签划分为结构化标签语义化标签功能化标签,文档标签。 读一遍MDN文档后的个人总结,重点在于整理语义化标签的定义规范,记录各种部件容易被忽略的特性。 关于HTML HTML的作用可以简单总结为结构化、语义化和提供基础...

    stonezhu 评论0 收藏0
  • 【前端芝士树】Array的属性及方法整理(参照MDN)

    摘要:本文主要是我自己对的一些整理,参考自,其中的分类有些不准确之处,还望见谅的基本属性属性的一些方法增删改查基础功能增删改查基础功能增删改删除数组的第一个元素删除数组的最后一个元素在数组的开头一个或多个元素,在数组的末尾增加一个或者多个元素数组 本文主要是我自己对Array的一些整理,参考自MDN,其中的分类有些不准确之处,还望见谅 Array const arr = [1, 2, 3, ...

    MoAir 评论0 收藏0
  • 芝士整理】浏览器存储

    摘要:维护浏览器和服务器端会话状态的一种方式,一般用于保存用户身份信息。服务器端生成推送到浏览器端,浏览器负责保存和维护数据。 Cookie 维护浏览器和服务器端会话状态的一种方式,一般用于保存用户身份信息。 服务器端生成Cookie推送到浏览器端,浏览器负责保存和维护数据。 特点 域名下的所用请求都会带上Cookie 每条Cookie限制在4KB左右 Cookie在过期时间之前一直有效,若...

    whlong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<