《你不知道的JavaScript上卷》知识点整理与读书笔记_百度你不知道的js书知识

b = a + doSomethingElse(a \* 2);
console.log(b \* 3);

}
doSomething(2);//15


##### 3.3函数作用域


尽管前面的使用函数包裹代码块达到了隐藏的意义,但是在某些时候任然是有一些缺点。一是用来包裹隐藏的函数名污染了全局变量,然后必须显示的调用函数名。但是幸好我们可以使用立即执行函数来解决。



(function foo() {
var a = 3;
console.log(a);
})();
console.log(a);
//这里就可以让函数当作一个表达式,而不是函数声明
//这里就没有让foo放出去污染全局,因为他不是声明
//这里最后紧紧跟了一个(),表示立即调用,因此不用显示使用标识符调用
//当然把最后的()放进去的效果也是一样的,仅仅是书写风格不同。如下
(function foo() {
var a = 3;
console.log(a);
}());
console.log(a);


* 那么时候是函数表达式呢?


Good question


区分:书上的办法很简单,就是查看function关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果function是声明中的第一个词,那么就是一个函数声明,否者就是一个函数表达式。


上面的代码中(function foo(){…})()就不是function开头,这个时候就是作为表达式,而foo标识符就是被封锁在foo(){…}中…的部分中,所以就没有污染全局变量


* 匿名和具名  
 匿名函数就是没有名字的函数



setTimeout(function(){
console.log(“I waited 1 second”);
},1000);


这部分的片段代码中seTimeout中function()就是没有名字。所以就是匿名函数。


* 注意:之后函数表达式才能拥有这种匿名函数,函数表达式没有函数名。请看前面的代码,这里可是函数表达式哈。


##### 3.4块作用域


现在我们学会了词法作用域、函数作用域以及一些小的知识点。现在学习新的作用域、块作用域。


前面说过,使用函数作用域能够隐藏一段代码,而隐藏也就是具有隔离意思。把一段代码隔离到一个函数中,让其外界无法触碰。但是并不是使用外包函数就能解决。



//这行代码只是为了验证()中的var i,以及{}中的a,最后在外部均可以访问到这两个标识符

for(var i = 0; i < 10; i++){
console.log(“for里面调用i”,i);
var a = 5;
}
console.log(“for外面调用for里面的a”,a); //for外面调用for里面的a 5
console.log(“for外面for里面的i”,i);//for外面for里面的i 10


下面是使用函数包裹



(function(){
for(var i = 0; i < 10; i++){
console.log(“for里面调用i”,i);
var a = 5;
}
}())
console.log(“for外面调用for里面的a”,a);//VM22:7 Uncaught ReferenceError: a is not defined
console.log(“for外面调用i”,i);//Uncaught ReferenceError: i is not defined


是完成了封锁,可是这样情况并不简便,所以开发者为JavaScript提供了块作用域。我觉得其实就和完成了函数作用域一样的功能,封锁代码块在其{}中。


* let


let声明的变量,是会和当前的作用域进行绑定。



for(let i = 0; i < 10; i++){
console.log(“for里面调用i”,i);
var a = 5;
}
console.log(“for外面for里面的i”,i);//Uncaught ReferenceError: i is not defined


恭喜,i已经绑定在for体内了,是不是方便很多了呢。


注意let i,不仅仅是把i绑定到了for循环的{}中,准确的说是绑定到了每一个迭代中,这点十分十分重要。后面闭包时候会再次提到这个事。同时let声明的标识符不会进行提升,这一点后面的提升相关部分会进行说明


* const  
 const声明的变量,是会和当前的作用域进行绑定,并且这个变量的值是固定的,不能再更改
* try/cath  
 没想到吧,居然每一个cath分支都是一个块作用域,不相信的话,可以动手试一试


#### 第4章 提升


**首先得出结论**:


* 只有声明本身会被提升,而赋值或者其他运行的逻辑会留在原地。如果是提升改变的代码执行的顺序,会造成严重的破坏
* 声明包括变量声明以及函数声明
* 每个作用域都会进行提升
* 函数和变量提升时候,函数会优先。因为函数是一等公民
* let以及const声明的标识符不会提升



foo();//1
var foo;
function foo() {
console.log(1);
}
foo = function () {
console.log(2);
}


上面代码会被引擎理解为如下形式:



function foo() {
console.log(1);
}
foo();//1

foo = function () {
console.log(2);
}
foo();//2


所以函数是优先提升的。


#### 第5章 作用域和闭包


##### 5.1 总结


我喜欢开篇,直接就总结完。


闭包产生的2种情况


* 当函数作为另一个函数的参数
* 函数作为返回值返回



function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
var baz = foo();
baz();//2 朋友,这就是闭包效果


##### 5.2 循环和闭包


要说明闭包,for循环是一个常见的例子



for ( i = 1; i <= 5; i++) {
setTimeout(function timer(){
console.log(i);
}, i*1000);
}


这段代码再运行时候会每秒一次的频率输出5次6.


延迟函数的回调会在全部循环迭代结束的之后时候进行调用(请查询宏任务、微任务相关知识点),而不是每次迭代时候调用。所以最后调用i,但是i是公共的,并且值为最后一个循环决定的6。所以结果是5次6



> 
> 那怎么给每个迭代的版本获取一个实时的i,满足哪怕是最后循环迭代完再调用定时函数,但是每个定时函数都是调用自己版本的,而不是调用最后的公用6呢?
> 
> 
> 


那就是每次循环迭代时候,我们给每一个迭代都绑定一个i。如下所示,我们使用let让每一个i都再内部迭代进行绑定。



for(let i = 1; i <= 5; i++){
setTimeout(function timer(){
console.log(i);
},i*1000);
}


##### 5.3 模块


在js中的模块也是和闭包息息相关。


模块:


* 必须要有外部的封闭函数,该函数必须要被调用一次
* 封闭的函数至少要返回一个内部函数
* 使用立即执行函数配合有奇效



var foo = (function(){
var something = “cool”;
var another = [1,2,3];
function doSomething() {
console.log(something);
}
function doAnother() {
console.log(another.join(“!”));
}
return {
doSomething,
doAnother
}
})();
foo.doSomething();//cool
foo.doAnother();//1!2!3


### 第二部分 this和对象原型


#### 第1章 关于this


##### 1.1为什么要使用this


首先记住,this提供了一种更优雅的方式隐式的传递一个对象引用,从而使API设计更加的简洁,并且更加易于复用


##### 1.2对this的一些误解


* 误解一、指向自身  
 按照this这个单词的语意,我们总是会把他认为是指向自身,事实上有些时候确实如此,但是并不是总指向自身。分析下面的模式。



function foo(num) {
//记录count被调用的次数
this.count++;
}
foo.count = 0;
var i;
for(i=0;i<10;i++){
if(i>5){
foo(i);
}
}
//foo:6
//foo:7
//foo:8
//foo:9
console.log(foo.count);//0


是不是没想到,哈哈哈。我读到这里时候也是没想到,好像就是从来没有仔细想过这件事一样。首先这里解释一下,这里的this.count会在全局创建一个变量,值为NaN。至于为什么后面第2章在解释,这里只是为了说明this真的不是任何时候指向自己。下面是单独打印的结果。



(function foo(num) {
//记录count被调用的次数
this.count++;
console.log(this);//window,也就是全局
console.log(this.count);//NaN
}())


* 误解二、指向他的作用域  
 首先这里this有时候指向作用域,有时候又不是,但是明确的一点就是任何时候this都是不会指向他的词法作用域。因为词法作用域是属于引擎的,无法通过js代码进行访问。


首先明确一下前面的案例中foo()中的this是指向window,这里我们在前面的基础上改一改



function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log(this.a);
}
foo();//undefined


明明this是window,也就是全局,然后bar也在全局,this.bar()没问题,但是因为bar中有this,按照词法作用域,bar在foo中,this.a右查询,就会查询bar的作用域中的a,但是没有就向上面找,上面foo中有a,所以调用。可是事实呢,结果是找不到,说明this.a进行右查询之后压根没有往上,或者往上后查找失败。(学了后面知道,是没有往上,因为bar中的this不是指向bar作用域,而是全局,所以最顶层没有,也不往上,直接undefined。至于是为什么bar中的this以及foo中的this均为window,请看后面的章节)


#### 第2章 this全面解析


##### 2.1调用位置


调用位置就是函数在代码中被调用的位置,不是声明的位置。



function foo() {
function bar() {
}
bar();//在foo中声明,所以bar的调用位置在foo中
}
foo();//在全局中声明,所以foo内的调用位置在window上


##### 2.2绑定规则


* (1)默认绑定  
 this绑定在window上。这个时候满足:查看函数调用位置时候,函数是光秃秃的直接调用,比如前面2.1中的bar,尽管是在foo中调用,可以打印看看bar中this是不是指向window
* (2)隐式绑定  
 this绑定到某个对象上。这个时候满足,这个对象的一个属性是一个函数,并且调用这个函数属性时候,是通过对象.函数名()或者对象函数名调用。否者会出现隐式丢失。



var a = 3;//我是全局的3
function foo(){
console.log(this.a);
}
var obj = {
a:2,//我是对象的2
foo:foo
};

obj.foo();//2,this绑定的是obj
obj"foo";//2,this绑定的是obj

var bar = obj.foo;//注意,这里可不是调用,后面没有(),这里只是拿出来
bar();//结果是3,明明bar就是foo函数,
//但是注意,这里并不是我们说的调用方法的两种之一
//所以呀,绑定丢失。而这里,
//bar的调用位置是光秃秃的直接调用,
//所以这里this绑定到window上咯


* (3)显式绑定  
 this绑定到某个对象上,但是其变种不会产生隐式丢失。这种方法很粗暴,直接使用call(),或者apply()。这两个函数,直接修改this的绑定对象。



function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
foo.call(obj);//2。也就是把foo函数的this,绑定到obj上


可是在进行如下调用时候,也会丢失



var a = 3;//我是全局的3
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
foo.call(obj);

//下面是调用
var bar = obj.foo;
bar()//是3,哈哈哈


显示绑定的变种的核心思想都是外面包裹一个函数,每次调用这个包裹函数就好,哪怕是在调用位置光秃秃调用,都不会绑定到全局的window,因为调用包裹函数,每次自动调用一次apply或者call。下面是变种的写法。



//变种一,直接包裹一个函数
var a = 3;//我是全局的3
var obj = {
a:2,//我是对象的2
foo:foo
};
function foo(){
console.log(this.a);
}
function bar() {
return foo.call(obj);
}

bar();//2 所以每次这样就好,哈哈哈


* (4)new绑定  
 首先JavaScript中new和其他语言中的new是完全不同。  
 JS中的new:使用new来调用构造函数,但JS中构造函数不属于某个类,也不会实例化某个类。(关于更多的解释,在第6章)



> 
> new的机制为:  
>  创建一个全新的对象  
>  这个新对象执行[原型]链接  
>  这个新对象绑定到函数调用的this中  
>  如果函数没有返回其他对象,new自动返回这个新对象
> 
> 
> 



function foo(a){
this.a =a ;
}
var bar = new foo(2);
console.log(bar.a);//2


##### 2.3优先级


不举例了,这里直接总结结论。


* new最大
* 显示
* 隐式
* 默认


##### 2.4箭头函数


箭头函数是ES6中新东西,不遵循上面的4条绑定规则。而是根据外层作用域决定的。



var a = 3;
var obj = {
a:2
};
function foo(){
((a)=>{
//this继承foo
console.log(this.a);
})()
}
foo()//3 这里光秃秃调用,foo中this是window,所以回调中是window的a



var a = 3;
var obj = {
a:2
};
function foo(){
((a)=>{
//this继承foo
console.log(this.a);
})()
}
foo.call(obj)//2 这里显示调用,foo中this是obj,所以回调中是obj的a


#### 第3章 对象


##### 3.1对象定义的语法


有两种方式,一种是通过声明形式,一种是通过构造形式



//声明
var obj = {
key:value
}

//构造
var obj = new Object();


##### 3.2类型


对象有6种基本类型


* string
* number
* boolean
* null
* undefined
* object  
 当然这些基本类型本身不是对象,只是说对象是根据基本类型划分有这么几个类型。不明白的可以继续看下去。


内置对象  
 JS中有许多特殊的对象子类型,被成为内置对象。下面的一些内置对象的名字和基本类型相似,但是不是同一个东西。下面的是实实在在的一个对象,不像前面的基本类型只是判别标准


* String
* Number
* Boolean
* Object
* Function
* Array
* Date
* RegExp
* Error  
 这些内置的对象好比java语言中的包装类,但是你先明白JavaScript中的类都是函数,是通过函数表达类的表象(这是后话,后面的第四章会有详细的类的讲解)


##### 3.3对象中的内容


**首先要强调一点,我们说的内容是,似乎暗示说这些值在对象内部,但是其实不是。如果是学过c语言或者c++的指针,或者明白说java中的引用。理解这一点是很容易的。在js中的对象的内容,对象中对的属性并不保存某些值,而是通过引用的方式,存放的是一些真正值的地址。**


书中的3.3.2中,再次阐述,一个函数与一个对象的关系。我通读完后,觉得其实就是任然说明和前面this隐式绑定一个对象的道理一样。一个函数无论如何也不要理解为属于一个对象。我们应该理解为这个对象拥有这个函数,或者说这个对象目前是这个函数的落脚点。毕竟,在对象中,如果某个属性是函数,那么这个属性保存的值其实是这个函数的引用而已。


特别注意数组也是js中的子对象Array。然后尽管是数组按照组织下标的方式存储数据,但是你也可以为数组添加key:value的形式的内容。还记得吗,数组不是有一个length属性吗,这个就是最好的例子。



var array = [“foo”,“22”];
array.bar = “bar”;
console.log(array.bar)//bar


现在关于引用,探讨对对象的内容的拷贝。因为引用的存在,所以出现了深浅拷贝。浅拷贝是拷贝引用,深拷贝而是彻底的进行复制一份数据。而在修改,数据时候,又牵扯到属性描述符以及setter与getter。下面的这两篇博客中部分内容对此进行了总结。


[对象]( )


[getter与setter]( )


##### 3.4对象中的遍历


for…in循环可以用来遍历对象的可枚举属性列表(包括[prototype]链),但是如何遍历属性的值???对于数组来说:



var array = [1,2,3];
for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}


这可不是遍历数组的值,这是遍历数组的下标,而不是值!!!  
 通过这个说明了通过for…in遍历值是不行。但是对于数组而言有一些方法可以进行值的遍历,(针对数组的遍历的方法,由于不是这一节的重点,不总结进来)


但是对与其他的对象呢?ES6中就专门增加了for…of结构遍历值(当然数组也可使用这个哈)



var array = [1,2,3];
for(var v of array){
console.log(v);
}
//1
//2
//3


好神奇,成功了。可是这种for…of的核心是怎么工作的呢?



> 
> for…of会首先向被迭代对象请求一个迭代器对象,然后通过调用迭代器对象的next()方法来遍历所有的返回值。
> 
> 
> 


**某种数据结构中必须要有@@iterator,才可以给for…of一个迭代器对象**


数组中有内置的@@iterator,所以可以给for…of一个迭代器对象



var array = [1,2,3];
var it = arraySymbol.iterator;

it.next();//{value: 1, done: false}
it.next();//{value: 2, done: false}
it.next();//{value: 3, done: false}
it.next();//{value: undefined, done: true}


* 我们使用Symbol.iterator获取@@iterator内部属性,关于点击这里[Symbol]( ),请记住引用类似的类似iterator特殊属性时候,要使用符号名array[Symbol.iterator],而不是单独一个array[iterator]
* array[Symbol.iterator]也就是@@iterator本身不是迭代器,而是一个返回迭代器的函数,所以上面的的代码中,通过后面()调用,返回一个迭代器,然后赋值给it


所以最后,让我们按照上面的2条,我们自己为一个遍历的对象实现一个迭代器



var obj = {
a:2,
b:3
}
//下面代码的意义在于给obj定义一个Symbol.iterator属性,这个属性是一个函数,用来返回迭代器
Object.defineProperty(obj,Symbol.iterator,{
enumerable:false,
writable:false,
configurable:true,
//看到了吗,这个属性值是一个函数
value:function () {
var o = this;//也就是这个对象本身,想一想为什么this是指向这个对象本身,我保证前面的this里面讲的知识,绝对可以分析出来这里的this指向哪里
var idx = 0;
var ks = Object.keys(o);//返回这个对象的键值列表
return{
next:function () {
return{
value:o[ks[idx++]],
done:(idx>ks.length)
}
}
}
}
})

//使用for…of遍历
for(var v of obj){
console.log(v);
}


* **这一章需要有一定的类的基础,不然读起来真的云里雾里。如果没有请先查阅其他资料补习。**


##### 4.1类理论


* 类/继承描述了一种代码的组织结构形式。
* 多态其实就是说父类的通用行为可以被子类用更加特殊的行为重写。(甚至相对多态允许我们从重写行为中引用基础行为)



> 
> 面向对象编程强调的是数据和操作数据的行为本质上是相互关联的,因此好的设计就是把数据以及它相关的相关行为打包(封装)起来,有时候这种情况被叫做数据结构。
> 
> 
> 


* 类只是一种模式,而不是必须的!当然java,c#这些语言,没得选,只能使用类模式。
* JavaScript中并不是必须采用的类模式。现在JavaScript中的“类”,也只是近似类。记住一点,js的“类”和他们都不一样,这也是加双引号的原因。


##### 4.2类的机制


书中表达的观点是,类和实例对象之间的关系看作之间关系而不是间接关系更好。因为所谓的类的关系,都是复制而已。具体怎么复制,以及细节,看下面。



> 
> 构造函数就是复制的关键点。他的目的就是完成复制的关键。术语叫做初始化对象。
> 
> 
> 


构造函数的特点


* 使用new来调用构造函数
* 函数名与类名相同
* new的过程,请看前面讲解this时候笔记


##### 4.3类的继承


**下面的书中的例子,是我觉得这个章节中最最最精彩的部分**  
 下面的**伪代码**例子说明了2点:


* 1、在js中的继承就是复制!!!
* 2、js中的多态!!!
* 3、js中的相对多态!!!



//下面几个类均不含构造方法

class Vehicle{ //交通工具类
engines = 1 //交通工具的属性

ignition(){//交通工具的方法
    output("Turning on my engine , Vehicle!")
}

drive(){//交通工具的方法
    ignition();
    output("Steering and moving forward")
}

}

class Car inherits Vehicle{ //一个继承交通工具类的汽车类
wheels = 4//汽车的属性

drive(){//这里就是子类重写父类的方法
    inherited:drive();//这里就是子类引用父类基础行为,这就是相对多态
    output("Steering and moving forward")//这里就是子类更加特殊的行为,这就是多态
}

}

class SpeedBoat inherits Vehicle{ //一个继承交通工具类的快艇类
engines = 2//快艇的属性

ignition(){//快艇的方法 这里就是子类重写父类的方法
    output("Turning on my engine ,SpeedBoat!")//这里就是子类更加特殊的行为,这就是多态
}

drive(){//快艇的方法 子类重写父类的方法
    inherited:drive();//这里就是子类引用父类基础行为,这就是相对多态
    output("SpeedBoat through the water with ease")//这里就是子类更加特殊的行为,这就是多态
}

}


好啦,上面的2、3点都写在注释中了,可是“在js中的继承就是复制!!!”,我们还没有得到解释。


仔细看一个有趣的点:


* (1)SpeedBoat中有一个drive方法对吧
* (2)这个是引用的方法对吧
* (3)所以SpeedBoat中的drive方法来自父类,自然里面的代码是这样

drive(){//交通工具的方法

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

ES6

  • 列举常用的ES6特性:

  • 箭头函数需要注意哪些地方?

  • let、const、var

  • 拓展:var方式定义的变量有什么样的bug?

  • Set数据结构

  • 拓展:数组去重的方法

  • 箭头函数this的指向。

  • 手写ES6 class继承。

微信小程序

  • 简单描述一下微信小程序的相关文件类型?

  • 你是怎么封装微信小程序的数据请求?

  • 有哪些参数传值的方法?

  • 你使用过哪些方法,来提高微信小程序的应用速度?

  • 小程序和原生App哪个好?

  • 简述微信小程序原理?

  • 分析微信小程序的优劣势

  • 怎么解决小程序的异步请求问题?

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-RMlyQbWY-1712874469674)]

ES6

  • 列举常用的ES6特性:

  • 箭头函数需要注意哪些地方?

  • let、const、var

  • 拓展:var方式定义的变量有什么样的bug?

  • Set数据结构

  • 拓展:数组去重的方法

  • 箭头函数this的指向。

  • 手写ES6 class继承。

微信小程序

  • 简单描述一下微信小程序的相关文件类型?

  • 你是怎么封装微信小程序的数据请求?

  • 有哪些参数传值的方法?

  • 你使用过哪些方法,来提高微信小程序的应用速度?

  • 小程序和原生App哪个好?

  • 简述微信小程序原理?

  • 分析微信小程序的优劣势

  • 怎么解决小程序的异步请求问题?

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-6PrdaQxC-1712874469674)]

  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值