《深入理解ES6翻译完整版》读书笔记

// 在循环中let声明每次都创建了一个新的i变量,因此在循环内部创建的函数获得了各自的i副本,
// 而每个i副本的值都在每次循环迭代声明变量的时候被确定了
var funcs = [];
for(let i=0; i<10; i++){
	funcs.push(function(){
		console.log(i);
	});
}
funcs.forEach(function(func){
	func(); // 从0到9依次输出
})
// 在ES6之前,JS的字符串以16位字符编码(UCS-2)为基础。是固定16位(双字节)的字符编码方式
// 判断字符包含了一个还是两个码元,对该字符调用codePointAt()方法就是最简单的方法
// 16位字符的上边界用十六进制表示就是FFFF,因此任何大于该数字的代码点必须用两个码元(共32位)来表示
functionis32Bit(c){
	return c.codePointAt(0)>0xFFFF;
}
// String.fromCodePoint()视为String.fromCharCode()的完善版本。两者处理BMP字符时会返回相同结果,只有处理BMP范围之外的字符时才会有差异。
// 当一个正则表达式设置了u标志时,它的工作模式将切换到针对字符,而不是针对码元。
// 字符串方法:includes() startsWith() endsWith() indexof() lastIndexOf() repeat()
// 正则表达式y标志影响正则表达式搜索时的粘连(sticky)属性,它表示从正则表达式的lastIndex属性值的位置开始检索字符串中的匹配字符
var text = "hello1 hello2 hello3",
	pattern = /hello\d\s?/,
	result = pattern.exec(text),
	globalPattern = /hello\d\s?/g,
	globalResult = gloabalPattern.exec(text),
	stickyPattern = /hello\d\s?/y,
	stickyResult = stickyPattern.exec(text);
// 只有调用正则表达式对象上的方法(例如exec()与test()方法),lastIndex属性才会生效。而将正则表达式作为参数传递给字符串上的方法(例如match()),并不会体现粘连特性。
// ES6的flags属性,会返回表达式中所有标志组成的字符串形式。
var re = /ab/g;
console.log(re.source);
console.log(re.flags);
// 字符串中包含反引号,在模板字面两种无需对双引号或单引号进行转移。
let message = `\`Hello\` world`;
console.log(message);
// 参数默认值中,null值被认为是有效的,不会使用默认值
// 默认参数为函数是,可能会返回可变的值
let value = 5;
function getValue(){
	return value++;
}
function add(first, second = getValue()){
	return first + second;
}
console.log(add(1,1));	// 2
console.log(add(1));	// 6
console.log(add(1));	// 7
// 剩余参数收到两点限制:1.函数只能有一个剩余参数,并且它必须被放在最后;2.剩余参数不能在对象字面量的setter属性中使用

// !!!Object.assign()并未在接收折上创建访问器属性,即使供应者拥有访问器属性。
// 由于Object.assign()使用赋值运算符,供应者的访问器属性就会变成接收者的数据属性,例如:
var receiver = {};
supplier = {
	get name(){
		return "file.js";
	}
};
Object.assign(receiver, supplier);
var descriptor = Object.getOwnPropertyDescriptor(receiver, "name");
console.log(descriptor.value);	// "file.js"
console.log(descriptor.get);	// undefined
// ES5通过添加Object.setPrototypeOf()方法允许修改任意指定对象的原型。
let person = {
	getGreeting(){
		return "Hello";
	}
};
let dog = {
	getGreeting(){
		return "woof";
	}
};
// 原型为person
let friend = Object.create(person);
console.log(friend.getGreeting());	// "Hello"
console.log(Object.getPrototypeOf(friend) === person); // true
// 将原型设置为dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreet());	// "woof"
console.log(Object.getPrototypeOf(friend) === dog); // true
// super是指向当前对象的原型的一个指针,实际上就是Object.getPrototypeOf(this)的值,!!!前提:super引用必须写在简写方法之内,不n能这样写getGreeting: function(){retur super.getGreeting() + ", hi!")}
let person = {
	getGreeting(){
		return "Hello";
	}
};
let friend = {
	getGreeting(){
		// 这相当于上个例子中:
		// Object.getPrototypeOf(this).getGreeting.call(this)
		return super.getGreeting() + ", hi!";
	}
};
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting());	// "Hello, Hi"
// 使用多级继承时,super引用是非常强大的,因为这种情况下Object.getPrototypeOf()不再适用于所有场景
// 任何对super的引用都会使用[[HomeObject]]属性来判断要做什么
// 解构destructuring,ES6的结构实际使用的语法是对象与数组的字面量语法
// "私有名称"意味着开发者可以创建非字符串类型的属性名称,由此可以防止使用常规手段来探查这些名称。
// 符号是基本类型的纸,可以用时typeof元素安抚来判断
let symbol = Symbol("test symbol");
console.log(typeof symbol);	// "symbol"
// 符号的描述信息被存储在内部属性[[Description]]中,当符号的toString()方法被显式或隐式调用时,该属性都会被读取。
let firstName = Symbol("first name");
console.log(firstName);	// "Symbol(first name)"
// 创建共享符号值,应用Symbol.for()方法而不是Symbol()方法
// Symbol.for()方法首先会搜索全局符号注册表,看是否存在一个键值为"uid"的符号值。
// 若是,该方法会返回这个已存在的符号值;否则,回传件一个新的符号值,并使用该键值将其记录到全局符号注册表中
// Symbol.keyFor()方法在全局符号注册表中根据符号值检索出对应的键值
let uid = Symbol.for("uid");
console.log(Symbol.keyFor(uid));	// "uid"
let uid2 = Symbol.for("uid");
console.log(Symbol.keyFor(uid2));	// "uid"
let uid3 = Symbol("uid");
// 1、WeakSet若调用add()方法时传入费对象的参数,就会抛出错误;2、WeakSet不开迭代,因此不能放在for-of循环中;
// 3、WeakSet无法暴露出任何迭代器(例如keys()与values()方法),因此没有任何编程手段可用于判断WeakSet的内容;
// 4、WeakSet没有forEach()方法;5、WeakSet没有size属性。
// 依然与Set类似,你能将数组传递给Map构造器,以便使用数据来初始化一个Map。
// 该数据的每一项也必须是数组,内部数组的首个项会作为键,第二项则为对应值。因此整个Map就被这些双向数组所填充
let map = new Map(["name", "Nicholas"], ["age", 25]);
console.log(map.size()); // 2
// 键允许是任意数据类型,将键存储在数组中,是确保它们在被添加到Map之前不会被强制转换为其他类型的唯一方法
// 当WeakMap中的键在WeakMap之外不存在引用时,该键值对会被移除。
// 必须注意的是,WeakMap的键才是弱引用,而值不是。在WeakMap的值存储对象会组织垃圾回收,即使该对象的其他引用以全部被移除。
// ES5中创建一个迭代器
function createIterator(items){
	var i = 0;
	return {
		next: function(){
			var done = (i>=items.length);
			var value = !done ? items[i++] : undefined;
			return {
				done: done;
				value: value
			};
		}
	};
}
var iterator = createIterator([1,2,3]);
console.log(iterator.next());	// "{value:1, done: false}"
console.log(iterator.next());	// "{vaue:2, done:false}"
console.log(iterator.next());	// "{value:3, done:false"
console.log(iteratro.next());	// "{value:undefined, done:true}"
// 之后的所有调用
console.log(iterator.next());	// "{value: undefined, done: true}"
// yield关键字只能用在生成器内部,用于其他任意位置都是语法错误,即使在生成器内部的函数中也不行,正如此例:
function *creeateIterator(items){
	items.forEach(function(item){
		// 语法错误
		yield item + 1;
	});
}
// 简单的类声明
class PersonClass{
	// 等价于ES5中的构造器函数
	constructor(name){
		this.name = name;
	}
	// 等价于ES5中的.prototype.functionName定义
	sayName(){
		console.log(this.name);
	}
}
let person = new PersonClass("Nicholas");
person.sayName();	// 输出"Nicholas"
console.log(person.instanceof PersonClass);	// true
console.log(person instanceof Object);	// true
console.log(typeof PersonClass);	// "function"
console.log(typeof PersonClass.prototype.sayName);	// "function"
// 1.类生命不会被提升,这与函数定义不同。类声明的行为与let相似,因为在程序的执行到达声明处之前,类会存在于暂时性四区内。
// 2.类声明中的所有代码会自动运行在严格模式下,并且也无法退出严格模式。
// 3.类的所有方法都是不可枚举的,这是对于自定义类型的显著变化,后者必须用Object.defineProperty()方法才能将方法改变为不可枚举。
// 相对于函数声明与函数表达式之间的区别,类声明与类表达式都不会被提升,。
// 类表达式可在内部使用const来定义它的不同名称。
// 可以使用Symbol.iterator来定义生成器方法,从而定义出类的默认迭代器
class Collection{
	constructor(){
		this.items = [];
	}
	*[Symbol.iterator](){
		yield *this.items.values();
	}
}
var collection = new Collection();
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for(let x of collection){
	console.log(x);
}
// 输出:
// 1
// 2
// 3
// 静态成员不能用实例来访问,需要直接用类自身类访问它们。
class PersonClass{
	constructor(name){
		this.name = name;
	}
	sayName(){
		console.log(this.name);
	}
	static create(name){
		return new PersonClass(name);
	}
}
let person = PersonClass.create("Nicholas");
// ES6实现继承
class Rectangle{
	constructor(length,width){
		this.length = length;
		this.width = width;
	}
	getArea(){
		return this.length*this.width;
	}
}
class Square extends Rectangle{
	constructor(length){
		// 与Rectangle.call(this,length,length)相同
		super(length,length);
	}
}
var square = new Square(3);
console.log(square.getArea());	// 9
console.log(square instanceof Square);	// true
console.log(square instanceof Recatngle);	// true
// 继承了其他类的类被称为派生类(derived classes)。如果派生类指定了构造器,就需要使用super(),否则会造成错误。
// 若选择不使用构造器,super()方法会被自动调用,并会使用创建新实例时提供的所有参数。例如:下面两个类是完全相同的:
class Square extends Rectangle{
	// 没有构造器
}
// 等价于:
class Square extends Rectangle{
	constructor(...args){
		super(...args);
	}
}
// Array使用了Symbol.species来指定方法所使用的类,让其返回值为一个数组。在Array派生出的类中,可以决定这些继承的方法应返回何种类型的对象
class MyArray extends Array{
	static get [Symbol.species](){
		return Array;
	}
}
let items = new MyArray(1,2,3,),
	subitems == items.plice(1,3);
console.log(items isntanceof MyArray);	// true
console.log(subitems instanceof Array);	// true
console.log(subitemes instanceof MyArray);	// false
// ES6 Array.of()方法在使用单个数值参数的时候并不会导致特殊结果
// 向Array.from()方法传递一个映射用的函数作为第二个参数,可以实现进一步的数组转换
function translate(){
	return Array.from(arguments, (value)=>value+1);
}
let numbers = translate(1,2,3);
console.log(numbers);
let uid = Symbol("uid");
console.log(Symbol.keyFor(uid));  // "uid"
let uid2 = Symbol("uid");
console.log(Symbol.keyFor(uid2)); // "uid"
let uid3 = Symbol("uid");
console.log(Symbol.keyFor(uid3)); // undefined
// Object.keys()与Object.getOwnPropertyNames()都不能返回符号类型的属性
// ES6新增了Object.getOwnPropertySymbols()方法,可以检索对象的符号类型属性


// 类似于fill()方法,如果向copyWith()方法传递负数参数,数组的长度会自动被添加到该参数的值上,以便算出正确的索引值。
// 与常规数组不同的是,不能使用length属性来改变类型化数组的大小。该属性是不可写的。
// from和of的区别 类型化数组和常规数组的区别
let ints = Int16Array.of(25, 50),
    floats = Float32Array.from([1.5, 2.5]);
console.log(ints instanceof Int16Array);  // true
console.log(floats instanceof Float32Array);  // true
console.log(ints instanceof Array); // false
console.log(Array.isArray(ints)); // false
// 由于类型化数组的大小不可变,因此push() shift() splice() unshift()这些方法都不能作用于类型化数组。
// concat()方法不可用的原因则是:连接两个类型化数组的结果是不确定的
// 类型化数组的两个特有方法:set() subarray()
// 类型化数组能够明显提升按位运算的性能,因为它不像JS的常规数值类型那样需要频繁进行格式转换。
// 事件循环(event loop)是JS引擎的一个内部处理线程,能监视代码的执行并管理作业队列。
// 既然是一个队列,作业就会从队列中的第一个开始,依次运行到最后一个。
// Node.js惯例:错误有限(error-first)的回调函数风格
readFile("example.txt", function(err, contents){
  if(err){
    throw err;
  }
  console.log(contents);
});
console.log("Hi");
// unhandledrejection:当一个Promise被拒绝、而在事件循环的一个伦茨中没有被任何拒绝处理函数被调哦那个,该事件就会被触发;
// rejectionHandled:若一个Promise被拒绝、并在事件循环的一个轮次之后再有拒绝处理函数被调用,该事件就会被触发。
let p1 = new Promise(function(resolve, reject){
  resolve(42);
});
let p2 = p1(function(value){
  console.log(value);
});
p2.then(function(){
  console.log("Finished");
});
// p2.the()的调用也返回了一个Promise,本例只是未使用此Promise.
let p1 = new Promise(function(resolve, reject){
  resolve(42);
});
let p2 = new Promise(function(resolve, reject){
  resolve(43);
});
p1.then(function(value){
  // 第一个完成处理函数
  console.log(value); // 42
  return p2;
}).then(function(value){
  // 第二个完成处理函数
  console.log(value); // 43
});
// 在所有的代理陷阱中,只有apply()与construct要求代理目标对象必须是一个函数。
// 函数拥有两个内部方法:[[Call]]与[[Construct]],前者会在函数被直接调用时执行,而后者会在函数被使用new运算符时执行。
// 模拟函数的默认行为
let target = function(){return 42},
    proxy = new Proxy(target,{
      apply:function(trapTarget, thisArg, argumentList){
        return Reflect.apply(trapTarget, thisArg,argumentList);
      },
      construct: function(trapTarget, argumentList){
        return Reflect.construct(trapTarget, argumentList);
      }
    });
// 使用了函数的代理,其目标对象会被视为函数
console.log(typeof proxy);  // "function"
console.log(proxy()); // 42
var instance = new proxy();
console.log(instance instanceof proxy); // true
console.log(instance instanceof target);  // true
// NumbersProxy函数允许调用Numbers而无需使用new,并且让这种调用的效果与使用了new的情况保持一致。
// 为此,apply陷阱函数使用传给自身的参数去对Reflect.construct()方法进行了调用,于是Numbers()内部的new.targe就被设置为Numbers(),从而避免抛出错误
function Numbers(...values){
  if(typeof new.target === "undefined"){
    throw new TypeError("This function must be called with new.");
  }
  this.values = values;
}
let NumbersProxy = new Proxy(Numbers, {
  apply: function(trapTarget, thisArg, argumentList){
    return Reflect.construct(trapTarget, argumentsList);
  }
});
let instance = NumbersProxy(1,2,3,4);
console.log(instance.values); // [1,2,3,4]
// 浏览器要求模块说明符应该当为下列格式之一:
// 以/为起始,表示从根目录开始解析;
// 以./为起始,表示从当前目录开始解析;
// 以../为起始,表示从父级目录开始解析;
// URL格式。
// 幂运算符
let result = 5**2;
console.log(result);  // 25
console.log(result === Math.pow(5,2));  // true
// 幂运算符的优先级在JS的二元运算中是最高的(但低于一元运算符)
// includes()方法使用===运算符来进行比较,仅有一个例外:NaN被认为与自身相等,尽管NaN===NaN的计算结果为false
let values=[1,NaN,2];
console.log(values.indexOf(NaN)); // -1
console.log(values.includes(NaN));  // true

猜你喜欢

转载自blog.csdn.net/taozi550185271/article/details/106488269