你不知道的js(上)总结

48 阅读7分钟

你不知道的js

块级作用域

let

let关键字可以将变量绑定在其所在的任意作用域({}),let隐式的为变量声明了作用域。且其不会想var一下进行变量提升,所以不能在let声明变量之前使用变量,因为声明变量之前还没有进行声明。

const

const和let一样可以为变量绑定作用域、但是其绑定的值是常量,是不能被改变的。

变量提升

var和函数声明都会有变量提升,先进行var变量声明再进行函数声明

作用域闭包

闭包就是函数的作用域中调用其他的函数作用域。导致调用的作用域无法被垃圾回收,形成了私有变量等。

this和原型对象

this在任何情况下都不指向词法作用域,也不指向自身。this:执行上下文

this绑定类型

  1. 默认绑定: 函数直接调用,它的this会指向window,在非严格模式下
  2. 隐式绑定: obj.fn() 这种隐形的绑定
  3. 显性绑定:apply、call、bind。
  4. new绑定: . 创建一个变量 ..把函数的原型指向变量的prototype ... 新对象绑定到函数调用的this执行函数 .... 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象

间接引用

function foo(){ console.log(this)}
const obj = {foo}
const obj1 = {foo}
(obj.foo = obj.foo)() // log: window
赋值返回的是应用值、所以是函数的引用被默认调用

被忽略的this

foo.apply(null,1) // window
foo.bind() // window
let toNull = Object.create(null) // 空对象
可以给函数绑定空对象

对象类型的属性访问和键访问

Obj = {a : 2}
obj.a // 2 属性访问
obj['a'] // 键访问
属性访问一定是连续字符串、键访问可以是任意值,然后会转换成字符串然后再进行访问
var myObject = { };
myObject[true] = "foo";
myObject[3] = "bar";
myObject[myObject] = "baz";
myObject["true"]; // "foo"
myObject["3"]; // "bar"
myObject["[object Object]"]; // "baz

深拷贝和浅拷贝

深拷贝: 引用类型的地址不同
浅拷贝: 引用类型的地址相同
Object.assign(..) 就是使用 = 操作符来赋值
如果深拷贝中有循环引用就会陷入死循环
列如两个对象相互引用进行深拷贝就不上那么容易拷贝了,可以把对象进行序列化,但是这样就取决于浏览器内核怎么把序列化的转换成相应的引用值,可能在不同的浏览器中间是确定的。
JSON.parse(JSON.stringify) 
​

Object.defineProperty

writable: 可写 值是否可修改
configtable 可配置 可以使用defineProperty进行配置且不能使用关键字delete进行属性
enmutable 可枚举 for ... in

对象:常量、禁止拓展

  1. 对象常量,可以使用writable:false以及configurable: false来是对象变成一个不可修改和删除的常量属性
  2. 禁止拓展: 可以使用Object.preventExtensions()来让一个对象停止加入新的属性
  3. 密封: Object.seal会使得对象完成不能添加新属性且无法删除,因为他会调用禁止拓展的函数并且使得每个属性的configurable=false
  4. 冻结: 调用密封然后在每个属性上的writable=false使得对象属性不可新增删除修改

Object赋值与屏蔽

let obj = {
    a: 2
}
let objOne = Object.create(obj)]
obj.a // 2
obj1.a // 2
obj1.a++ // 3
obj.a// 2
如果对象原型上有相同的属性会分为三种情况:如果对象没有改属性,而原型上有的话赋值会出行的情况
1. 而原型上有该属性的话,且该属性没有可读等直接在原有对象上进行赋值
2. 如果原型上有该属性且只是可读的话进行修改就不会屏蔽
3. 如果原型上有该属性且有设置setter赋值也不会被屏蔽

javascript中的构造函数有类似于类的功能,可以创建一个对象实例,这个实例的[[prototype]]指向构造函数的prototype。这种行为称作委托或者差异继承,就是一个构造函数可以构造出多个对象,每个对象的[[prototype]]都指向同一个对象,从而实现了一些属性的共有。
构造函数:在new执行的过程中的函数成为构造函数,创建实例对象的核心是Object.create({})
new的执行过程:
1. 创建一个对象
2. 把创建对象的proto指向函数的prototype
3. 把函数中的this替换成创建的对象执行函数
4. 返回对象

硬绑定函数没有prototype属性

created

Object.createt(target,{})用来创建一个空对象,空对象的prototype指向target,第二个参数可以给对象增加属性,以及改变属性描述符的是否可读等
​
// 实现的思路
function foo(t1) {
    const Foo = function(){}
    Foo.prototype = t1
    return new Foo()
}

委托行为

使用create生成原型,从而用另外一种方式实现继承实际上应该是委托

const foo = {}
const bar = Object.create(foo)
const doo = Object.create(bar)

对于上册的理解

上册总共有两章,第一章讲的是作用域相关的问题,第二章讲的是this指向和原型继承的问题

第一章有告诉我们

作用域是什么:作用域就是使用get操作的时候能够检索的区域就是作用域,而set操纵会在当前所在的作用域新增,也可以说是属性起作用的范围叫做作用域

RHS 查询与简单地查找某个变量的值别无二致,而 LHS 查询则是试图 找到变量的容器本身,从而可以对其赋值

作用域链是什么:作用域链,当作用域中含有块级作用域或者函数作用域的时候,就会形成作用域链,因为每一个块级或者函数都是一个作用域,函数中调用某个变量,在当前作用域没有找到的时候,会想包含函数作用域的父级作用域进行检索,以此类推就形成了一条作用域链。

作用域闭包:作用域闭包就是函数作用域中包含其他的函数作用域叫做闭包,包含父级作用域让闭包更加的明显。

第二章

this:名称是执行上下文,所谓执行上下文代表的时执行这个操作时的所属的对象,而不是当前对象本身。

如果在函数中有this的执行问题一般分为两个问题: 是默认执行还是对象执行
默认执行就是 fn执行的时候没有前缀, 指向的就是window默认对象
对象执行就是 obj.obj1.obj2.fn() 执行这个函数对象的this指向是执行时的作用域obj2
obj.value 代表的是在obj属性中找value,并且执行

类:讲述了js中的类不是真正意义上的类,真正的可以描述万物类的实例也有类的所有属性这是一种继承的关系,而js中的类是一种委托机制,是通过一种原型机制来实现的,所有的继承都是通过原型链来实现的。js中的类是一种语法糖,原型继承的语法糖。

原型:通过new fn() 构造函数来创建的实例对象,这个实例对象的原型就是这个构造函数的fn.prototype, fn.prototype.constructor = fn,创造的实例会有一个属性指向构造函数的原型,实例可以使用构造函数原型中的方法以及属性。

原型链:实例中找不到的属性可以原型对象中查找,原型对象中也没有就会在原型对象的基础上找他的原型,直到遍历完整个原型链为止。

形成原型继承只需要对象中有prototype属性就能形成原型继承,这就要使用到ctreate属性了。