rainy934 / notebook Goto Github PK
View Code? Open in Web Editor NEW技术笔记仓库
Home Page: https://rainy934.github.io/notebook/
License: MIT License
技术笔记仓库
Home Page: https://rainy934.github.io/notebook/
License: MIT License
词法
作用域 ,遮蔽效应,作用域查找&对象访问规则JS中this很难理解它的绑定,那我们为什么还需要用到它呢,可以不用么?
在执行代码时候,比如说函数中的需要取某个上下文的变量,一般来说我们是通过某种方式首先获取到这个上下文对象,然后可以取到变量;常见的可以通过两种方式取上下文对象:
在代码复杂度较大的情况下, 显式传入方式会导致代码很乱,很难维护
this
和词法作用域无关,this
的绑定和函数声明的位置没有任何关系
this
是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this
的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this
就是这个记录的一个属性,会在函数执行的过程中用到。 ——《你不知道的JS》
this
是在调用时被绑定的,完全取决于函数的调用位置
(也就是函数的调用方法)。默认绑定 无法应用其他绑定规则的时候使用的默认规则
隐式绑定 person.name()
方法调用位置存在上下文对象,this绑定person对象
显式绑定 name.call(person)
方法通过call
,apply
等方法调用,显式传入上下文对象
new绑定 new Person()
执行时方法中的this被绑定为一个新对象,这个对象prototype会链接Person的原型
显示绑定 call,apply等方法传入null,undefined等值,会被忽略,而不会取绑定到this
箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this。 ——《你不知道的JS》
一种程序设计语言必然会使用到变量,而优秀的变量的存储和访问的逻辑设计可以使得语言执行效率有很大提升,JS中的变量的存储和访问的系统称为其作用域系统,也可以说是存储和访问的规则
编译器读取js代码,识别到声明语句就通知作用域生成相关的变量,同时对于不是声明语句也就是需要执行的动作,编译器生成相关代码交给js执行引擎去执行,引擎执行代码过程中会访问作用域获取需要的变量,也更新一些变量
结论:js代码编译的时刻在执行之前,简单说,首先编译器遍历代码,声明变量,同时生成可执行代码,做好了准备,然后引擎开始执行
分为LHS和RHS,LHS查询是找到这个变量的目标位置,给其赋值,不关注其原来的值;RHS查询的是变量原来的值,然后可能会使用这个值,而不对变量作处理
一段代码块有它的作用域区域范围,如果存在一个代码块在另一个代码块内部呢?这种情况下,作用域被设计成是可嵌套的,内层代码执行时,访问变量,内层找不到就往外层找,一直找到全局作用域,若还不存在,则抛出异常
(非严格模式下)当变量没有被声明的情况下,这两种查询或者说引用方式,会导致不同的结果;LHS,作用域会好心自动生成这样的一个变量;RHS,抛出ReferenceError
作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。 --来自原书《你不知道的JS》
某个表达式(通常指函数)可以在其词法作用域外执行时访问其词法作用域
function A(){
var a = 1
function B(){
console.log(a)
}
return B
}
A()()
嵌套函数A包含B,B被通过某种方式(比如值传递)转移到词法作用域A()之外,当函数A执行完成之后,按常理来说当前作用域A应该被释放,但是,因为B使用了A()中的变量a,而且B没有被释放调,B可能会用到变量a,因为变量a有被引用次数,所以a不被释放。
我一直把闭包当作一种特性,当我需要一个封闭的,可长期存在的环境(作用域)的时候,可以考虑构造闭包环境来实现。
for(let i = 0;i < 10; i++) {
setTimeout(function timer(){
console.log(i)
})
}
for(var i = 0;i < 10; i++) {
(function(i) {
setTimeout(function timer(){
console.log(i)
})
})(i)
}
同时可以发现,使用IIFE可以模拟块级作用域哦
string
,number
,bollean
,null
,undefined
,object
String
, Number
, Boolean
, Array
, Object
, Function
, Date
, RegExp
, Error
以上这些不要当作类型看待,当作内置的特殊函数来看待
object是关键字,表示基本数据类型;Object是一个对象(构造函数)(Function类型的),可以被new运算符使用,构造对象实例
typeof 和 instanceof 这两个功能就是完全不一样的运算符。typeof 是为了检查数据类型,instanceof是为了看一个变量是否是某个对象的实例。
js当中数据无非就是那么集中,函数,字符串,数值类型,布尔类型
String
的实例,所以基本你可以看见的字符串本质上也是对象实例(构造函数是String)JSON.parse(JSON.stringify(obj))
要求obj必须是JSON安全的举例,obj.a // undefined, 如何知道对象obj是否定义过属性a(a的值为undefined),还是没有定义过a
a in obj
检查属性是否在对象及其原型链中hasOwnProperty
检查对象独有的属性,不检查原型,因为hasOwnProperty
是原型函数,如果存在对象没有链接原型(Object.create(null)), obj.hasOwnProperty('a')
会报错,所以建议使用Object.prototype.hasOwnProperty.call(obj, 'a')
I/O 密集型 -- 项目当中处理任务多,输入输出频繁的应用场景。
Cpu 密集型 -- 项目当中任务数量较少,不频繁生成, 但是单个任务的计算量非常大,比如图像处理分析,或者大数据量的筛选处理等。
Node解决CPU密集型虽然比较麻烦,但是其中关键在于合理调度服务器资源
在作用域一节提到过js的执行顺序,先收集声明,做好准备,然后编译代码,再然后执行。也就是说,所有的声明语句无论写在代码的什么位置,在编译执行的时候,都会优先’执行‘,这就是所谓的提升,更精确的说是声明提升
var a = 2
对于这行声明赋值语句,我们必须要有个认识,可以分解成一下:
var a // 声明语句
a = 2 // 赋值语句
声明语句和赋值语句有个区别,声明语句会进行提升,赋值语句不会
代码:
console.log(a) // undefined
var a = 2
实际上:
var a
console.log(a) // undefined
a = 2
代码:
a = 2
var a
console.log(a) // 2
实际上:
var a
a = 2
console.log(a) // 2
代码:
foo() // 2
var foo = function() {console.log(1)}
function foo() {
console.log(2)
}
实际上:
function foo() {
console.log(2)
}
foo() // 2
foo = function() {console.log(1)}
函数声明在前,变量声明在后
词法阶段的作用域,作用域之间的位置关系由词法阶段代码块的位置来决定
每一个函数区域会生成一个作用域;遮蔽效应指内外层作用域存在同名变量,内层访问只能访问到内层变量,无法访问到外层变量
注意:可通过window 全局变量来间接访问
eval
动态创建执行代码with
改变代码块内的作用域环境这两个机制的副作用是引擎无法在编译时对作用域查找进行优化,因为引擎只能谨慎地认为这样的优化是无效的。使用这其中任何一个机制都将导致代码运行变慢。不要使用它们。 —— 《你不知道的JS》
作用域可以互相嵌套,一层套一层,那么什么情况下会生成一层作用域呢?什么样的结构会生成?
固有的最外层作用域
函数结构会生成作用域,嵌套函数自然就是嵌套作用域
普通函数的声明执行会导致污染所在的作用域,如果你不想这样可以使用立即执行函数方式
(function foo(){
var a = 3
console.log( a )
})()
在一些需要回调函数的场景中,匿名函数使用的比较多,但是不利于调试,而且想要复用也不太方便(过期的arguements.callee)
setTimeout( function () {
console.log( "I waited 1 second!" )
}, 1000 )
最好还是养成命名的习惯
setTimeout( function timeLoad() {
console.log( "I waited 1 second!" )
// timeLoad()
}, 1000 )
with
创建块级作用域try/catch
let
let是ES6的变量声明方式,只不过和var不同的是,let 声明的变量只存在于当前代码段{...}
中,并且不允许重复声明,本质上来说,let就像一个强盗,它劫持了这个代码段这里面的变量声明它说了算,但仅仅在这个代码段有效
if (foo) {
{ // 显式的块
let bar = foo * 2;
bar = something( bar );
console.log( bar );
}
}
console.log( bar ); // ReferenceError
递归是一种用自己定义自己的一种**,比如斐波那契数列,举例来说: 存在f(0) = 1, f(x) = f(x-1) + f(x - 2);
需要得出f(x) x >= 0, 在这非负整数集合内的序列
解决方案:
递归是一种**,使用它便于理解算法**,也能使得程序代码简洁。但是算法使用必须要有其实用性,在这里递归的方式,对于求解斐波那契数据列效率是非常低下的。
递归应用基本原则:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.