暂时性死区
Javascript在es6中提出了暂时性死区,其本质是:
只要一进入当前作用域,用let声明的变量实际上就已经存在了,但是需要等到let声明变量之后才能使用,相比较es5,使用var声明的变量会存在变量提升的问题,即使用var声明的变量在声明之前也是可以使用的,其值为undefined。不存在变量提升的情况下,这样做是会直接报错的。
es6中的块级作用域
考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
函数声明
{
let a = ‘abc’;
function f (){
return a;
}
}
函数表达式:
{
let a =’and’;
let f = function(){
return a;
}
}
do表达式
本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值:
{
let t = f();
t = t * t +1
}
在块级作用域之外,没有办法得到t的值,因为块级作用域不返回值,除非t是全局变量现在有一个提案,是得块级作用域变为表达式,办法就是在块级作用域前加上do,变为do表达式:
let x = do{
let t = f ();
t = t +1
}
上面的代码中x会得到整个块级作用域的返回值。
const命令
const声明一个只读常量,和java相似,常量不能被改变。
const的作用域和let相同,只在块级作用域内有效
if(true){
const MAX = 5;
}
MAX
这样做会报错
const本质:const实际上保证的不是变量的值不能改动,而是变量指向的内存地址不得改动,对于简单数据类型,值就保存在变。量指向的那个内存地址,等同于常量。但是对于复合类型的数据(对象和数组),变量指向的是内存地址,保存的是指针,const只能保证指针是固定的,但是其指向的数据结构是可变的。
const foo = {}
//可以改变数据结构
foo.prop =123;
foo.prop //123
//不可以改变指针指向的地址
foo = {} //会报错 foo is read-only
如果想要使对象不可变,应该使用Object.freeze方法:
const foo = Object.freeze({});
foo.prop = 123 //严格模式下会报错,一般迷失下不会起作用
(在java中可以通过将对象中的属性声明为private使外界不能修改Java对象)
顶层对象的属性
顶层对象,在浏览器中指的是window对象,在node中指的是global对象。
顶层对象的属性和全局变量挂钩,被认为是js最大的设计败笔。这样的设计会导致几个很大的问题,首先是没法在编译的时候就报出变量未声明的错误,因为全局变量可能是顶层对象的属性创建的,而属性的创造是在程序在运行时动态生成的,这样会导致,代码没有跑到的地方,错误就发现不了(对比java的静态检查,简直太人性化了),刚好顶层对象的属性又是可以到处读写的,这样非常不利于模块化编程,因为模块化的基本要求就是要相对独立,所以如果用js写大型项目将会变得很危险,可能给变量取个合适的名字都会成为一个问题。
es6为了改变这一点作恶了什么事情呢?
var 和function声明的全局变量依然是顶层对象的属性(为了兼容性),let const class命令声明的全局变量不属于顶层对象,可以看到js在慢慢的变好。
global对象
js之所以号称坑最多的语言,不是没有道理的,因为在es5中顶层对象的各种实现里面是不统一的。
-在浏览器中,顶层对象是window,在node和web worker中没有window
-浏览器和webworker中,self指向顶层对象,但是node没有self(self不是python中的保留字么,js什么时候用到了这个玩意,)
-node中的顶层对象是global
同一段变量为了能够在各种环境中都能取到顶层对象,使用this变量会好一点,这个是统一的,但是也有局限性。
-全局环境中,this会返回顶层对象,但是node和es6模块中,this返回的是当前模块。(没毛病,这样做是合理地)
-函数里面的this,如果函数不是作为对象的方法运行,而是单纯的作为函数运行,this指向顶层对象,但是在严格模式下this会返回undefined‘
-不管是严格模式还是普通模式,new function(){return this},总司会返回全局对象,但是如果浏览器使用了安全政策CSP(Content Security Policy),那么eval,new function 都可能无法使用。eval(string)该函数接收一个原始字符串,并计算字符串的值,字符串应该包含js表达式。
// 方法二
var getGlobal = function () {
if (typeof self !== ‘undefined’) { return self; }
if (typeof window !== ‘undefined’) { return window; }
if (typeof global !== ‘undefined’) { return global; }
throw new Error(‘unable to locate global object’);
};
es6变量的解构赋值
es6支持模式匹配(python也支持,Java不支持这种赋值的方式),用法:
let [a,b,c] = [1,2,3]
//a = 1
只要等号的两边模式相同,就能够按照既定的规则进行赋值,如果解构失败,相应位置上的值就是undefined。
let [foo] = [];
let [bar, foo] = [1];
foo的值都是undefined,而bar的值是1
不完全解构:
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
在什么情况下解构会失败?
当等号右边的值是一个不可迭代对象(没有iterator接口),那么解构会失败。
事实上,只要某种数据解构只要具有iterator接口,都可以采用数组形式的解构赋值:
function* fibs(){
let a = 0;
let b =1;
while(true){
yield a; //函数会返回此时的a,但是操作数栈依然存在
[a,b] = [b,a+b];
}
}
let[frist,second,third,fourth,fifth,sixth] =fibs();
six //5
呵呵,这特么不就是python里面的生成器么,fibs()函数就是斐波那契数列。什么是斐波那契数列?斐波那契数列指的是数列的下一个值始终等于前面两个值的和。在java中要生成斐波那契数列稍微麻烦一点,因为一旦函数返回值,就出栈了。但是要实现打印斐波那契数列还是不难,那么如果要用Java实现一个斐波那契数列应该怎么办呢?
思路:斐波那契数列的数学表达式可以抽象为[fn,f(n-1)+f(n),f(n-1)+fn+f(n+1)]
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
System.out.println(createFibArr(10));
}
private static ArrayList createFibArr(int n){
ArrayList fibs = new ArrayList();
Integer num = 0;
for (int i=1;i<=n;i++){
num = getFibNum(i);
fibs.add(num);
}
return fibs;
}
private static int getFibNum(int n){
if(n <= 2){
return 1;
}else{
return getFibNum(n-1) + getFibNum(n-2);
}
}
}