es6
let /const
const 值不可修改, 比如先const x = 1; 再 x = 2; 就会报x is readonly
解构赋值
-
字符串、函数参数都可以解构
-
可以直接用取到的nodeList了!不用Array.prototype.slice.call(..)啦!!
ps: 现在还可以直接用Array.from()转数组喔!
-
对象展开符的妙用
-
replace apply
- 操作一些数组
- 浅复制对象/数组
字符串的扩展
- startsWith, endWith, include
import & export
set and map
两种可以看做是array和obj的升级版
-
Set: Set数据结构中所有成员的值都是唯一的,没有重复的值。判断重复的标准是===
-
add, delete, has, clear
- Array.from 可将 Set 结构转变为数组
- keys, values, entries, forEach
-
数组去重: return Array.from(new Set(array))
-
Weakset:
-
WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别:① WeakSet 的成员只能是对象,而不能是其他类型的值。② WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
-
没有size和forEach属性
-
用处:储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。
-
关于为什么不会内存泄漏有疑问: http://www.ruanyifeng.com/blog/2017/04/memory-leak.html 难道其余的对象不被引用时内存就不被释放了吗?
-
Map
- WeakMap
proxy
let p = new Proxy(target, handler)
其中handler有如下几种:
{
get: function(target, property) {},
set: function(target, property, value, receiver) {}
}
//可以用来操纵p的读取值
//在new Proxy时,target的所有属性会复制给b一份
//b新增减属性时 target也会同样接受这个操作 增减属性
可以验证向一个对象的传值:
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
}
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age = 'young';
// 抛出异常: Uncaught TypeError: The age is not an intege
还可以对想要读取或修改私有变量做限制~
同样是在handlers.get+handlers.set或者直接是handlers.has中判断key,若key名是被限制的key名,那么则拦截操作。
否则执行Reflect.get(...) /set(...) / has(...)
generator&iterator
iterator迭代器
In JavaScript an iterator is an object that provides a next() method which returns the next item in the sequence.
这个方法返回两个值 done和value
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
}
ar it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done); // true
Iterables**(可迭代对象)为:Array, TypedArray, String, Map, Set;它们.prototype上都有一个[Symbol.iterator]属性 即xx.prototype[Symbol.itrator] !== undefined
Generators生成器 => 可用来生成*iterables*!
生成器对象是由一个 generator function 返回的,并且它符合可迭代协议和迭代器协议。
function* gen() {
yield 1;
}
let g = gen()
Object.getPrototypeOf(g) //=> Generator {}
解释:
generator function即function*:会定义一个生成器函数,它返回一个Generator生成器对象(即一个指向内部状态的指针对象,通过调用 next 方法,指针会移向下一个状态)
调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的迭代器(iterator)对象。迭代器的 next() 方法的作用是分阶段执行 Generator 函数,yield 后紧跟迭代器要返回的值。
调用 next()方法时,如果传入了参数,那么这个参数会作为上一条执行的 yield 语句的返回值。也就是说本来生成器函数是一个包裹住内在状态的函数,但是使用者是可以染指这个内部状态的!~
或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。
ps: 可以将一个数组转换成一个iterable<不是通过变Set..> 我放到刷题里拉 这题!
class Box {
constructor (stuffs = []) {
this.stuffs = stuffs
}
[Symbol.iterator] () {
let i = 0
return {
next: () => {
if (i >= this.stuffs.length) return { done: true }
else return { value: this.stuffs[i++], done: false }
}
}
}
}
pps:
※ Generator函数,不管内部有没有yield语句,调用函数时都不会执行任何语句,只有当调用next(),内部语句才会执行,只要调用next(),就会返回一个对象。yield语句只是函数暂停执行的一个标记
※ for...of循环可以自动遍历Generator函数时生成的Iterator对象,且此时不再需要调用next方法
function *foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 4+1; //只有在获取第五个元素时才会计算
return 6;
}
for (let v of foo()) {
console.log(v) // 1 2 3 4 5
}
作用:
迭代器Iterator, infinite range, 暂停函数, lazy evaluation, 来实现async/await
阮的异步举例:
var fetch = require('a 封装好的 fetch promise');
function* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url);
console.log(result.bio);
}
// 使用:
// var r = gen().next()
// 写错!应该是
var g = gen(); var r = g.next()
r.then(res => {
rg.next(res)
})
ref: http://www.ruanyifeng.com/blog/2015/04/generator.html
箭头函数
恩 箭头函数里还没有arguments 因为rest对象展开符可以实现arguments的作用 所以功能上也不需要它了!
var bar = (...arguments) => console.log(arguments);
另外 箭头函数要实现类似纯函数的效果,必须剔除外部状态。所以当你定义一个箭头函数,在普通函数里常见的this、arguments、caller是统统没有的。
所以以下情况不要用箭头函数:
。。。
通过以上说明,我们可以看出,箭头函数除了传入的参数之外,真的是什么都没有!如果你在箭头函数引用了this、arguments或者参数之外的变量,那它们一定不是箭头函数本身包含的,而是从父级作用域继承的。另外,也不能用call(), apply(), bind()去改变this的指向
哈哈哈。
关于 this
呀呀呀关于this又要晕乎乎了!
这是原来普通函数的老情况:
一。函数调用
+ 比如parseInt('18')>函数调用; [2,4].join('')>方法调用
+ this在函数调用中是一个全局对象Window //!!无论是哪里的函数!都是!
function sum(a, b) {
console.log(this === window); // => true
this.myNumber = 20; // add 'myNumber' property to global object
return a + b;
}
// sum() is invoked as a function
// this in sum() is a global object (window)
sum(15, 16); // => 31
window.myNumber; // => 20
+ this在strict的函数里 是undefined
+ 陷阱: 内部函数中的this
var numbers = {
a:1,
b:2,
add: function(){ //方法调用
console.log(this === numbers); //true //箭头函数: false。所以这里和底下的子函数都最好写成numbers.a和numbers.b
function cal(){ //函数调用
console.log(this === numbers); //false
return this.a + this.b
}
return cal(); //undefined
//解决方法 return cal.call(this);
}
}
但是对于“箭头函数”的this,箭头函数是没有this的,箭头函数里使用this就像使用普通变量一样,在箭头函数的scope内找不都会一直向父scope寻找。来源: 看到这里的讨论
这个例子里 setTimeout定义时的上下文是foo 其.id = 42; settimeout执行时的上下文是全局window 其id是21。
所以this.id === 定义时上下文的id === 42
关于setTimeout里的this: 原来的this在函数调用中是一个全局对象 如左解释
然而箭头函数中:由它的外层函数决定
二。方法调用
+ this在方法调用中是拥有这个方法的对象
+ 当对象从prototype继承的方法在新对象上被调用时,上下文依然是该对象本身
var myDog = Object.create({
sayName: function(){
console.log(this === myDog); //true //被分离出来的方法: false 箭头函数: false。
return this.name;
}
});
myDog.name = 'Milo';
myDog.sayName(); //'Milo'
var sayname = myDog.sayName
sayname() //undefined
+ 陷阱: 从object中分离方法
function Animal(type, legs){
this.type = type;
this.legs = legs;
this.logInfo = function() {
console.log(this === myCat);
console.log(this.type + this.legs);
}
}
var myCat = new Animal('Cat',4);
setTimeout(myCat.logInfo, 1000); //undefined
//解决方法 setTimeout(myCat.logInfo.bind(myCat), 1000);
//这儿的myCat就是一普通对象 相当于var xx = myCat.logInfo; 然后再xx()也是undefined一样。方法被从对象身上分离出去了。
三。构造函数
+ this在构造函数调用中指向新创建的对象
+ 陷阱:忘了new
function Vehicle(type, wheelsCount) {
this.type = type;
this.wheelsCount = wheelsCount;
this instanceof Vechicle
return this;
}
var car = Vehicle('Car', 4);
this //=> Window
car.type; //=>car;
car === window // => true
∴ instanceof Vechicle => false
var car = new Vehicle('Car', 4)
this //=> Vechicle {type: Car, wheelsCount: 4}
四。隐式调用
+ 当函数被.call()或.apply()调用时 执行的是隐式调用
+ 方法.call(thisArg[, arg1[, arg2[, ...]]])将接受的第一个参数thisArg作为调用时的上下文,arg1, arg2, ...这些则作为参数传入被调用的函数。方法.apply(thisArg, [args])将接受的第一个参数thisArg作为调用时的上下文,并且接受另一个类似数组的对象[args]作为被调用函数的参数传入。
+ 在隐式调用中, this是第一个参数
var rbt = {name: 'white rabbit'};
function concatName(string){
console.log(this === rabbit) //=>true
return string + this.name;
}
concatName.call(rbt,'hello');
concatName.apply(rbt,['bye']);
∴隐式调用可以用于模拟在一个对象上调用某个方法
五。绑定函数
+ 绑定函数是一个与对象绑定的函数。通常它是通过在原函数上使用 .bind()来创建的。原函数和绑定的函数共享代码跟作用域,但是在执行时有不同的上下文。
+ 方法.bind(thisArg[, arg1[, arg2[, ...]]])接受第一个参数thisArg作为绑定函数执行时的上下文,并且它接受一组可选的参数 arg1, arg2, ...作为被调用函数的参数。它返回一个绑定了thisArg的新函数。
function multiply(number){
'use strict';
return this*number;
}
var double = multiply.bind(2);
double(3) //=>6
跟.apply()以及.call()方法(见5.)马上调用函数不同,.bind()函数返回一个新的方法,它应该在之后被调用,只是this已经被提前设置好了。
+ this在调用绑定函数中是.bind()的第一个参数,并且非常强大难以被改变。 .bind()永久性地建立了一个上下文的链接,并且会一直保持它。一个绑定函数不能通过.call()或者.apply()来改变它的上下文,甚至是再次绑定也不会有什么作用。 只有用绑定函数的构造函数调用方法能够改变上下文,但并不推荐这个方法(因为构造函数调用用的是常规函数而不是绑定函数)。
+ .bind()总是会在forEach循环中使用~~比如:
DOMTokenList.prototype.adds = function(tokens) {
tokens.split(" ").forEach(function(token) {
this.add(token); //这里的this就是DOMTokenList的实例化对象 否则会是window
}.bind(this));
return this;
};
①注意这里用的是.bind() 因为forEach()里面要求传的是一个function参数,每遍历一次js就会执行一次这个参数。如果用的是call(),那么传入forEach()时就会执行了,那么参数就不是function格式了,就不对。
②我模拟的forEach:
Array.prototype.myEach = function(f){
var length = this.length;
for(var i = 0; i<length; i++){
f(this[i],i); //也给传入的function传了两个值。第一个值是value, 第二个值是index
}
}
∴for循环与forEach大概有两个最明显的不同点(都与for循环的特性相关):**
①for循环不需要bind,但是forEach需要~
②for循环可以break, forEach不可以
③JS权威指南p218模拟的forEach,直接在调用传入的参数时就使用了call 还多了一个可以传context的参数
Set.prototype.foreach = function(f, context){
for(var s in this.values) f.call(context, this.values[s]) //注意这里用的是call 并传入了value值
}
我一直很想弄清楚构造函数和 Class 的异同
构造函数:
function Test(a){
this.a = a
this.b = function(s){
//this指的是被实例化的对象
return this.a
}
}
const rootProto = {
pa: 'fff',
pb: function(s){
return this.a
}
}
Test.prototype = rootProto
const t = new Test('boom')
Object.getPrototypeOf(t) // rootProto也就是{pa: 'fff', pb: f}
rootProto.isPrototypeOf(t) //true
for (props in t) {
// a b pa pb。a,b是自身属性,pa pb是继承属性。
}
Object.getOwnPropertyNames(t) // ['a', 'b']
//以下!是个关于this的需要注意的地方!
t.b() //boom
const tb = t.b
tb() //undefined
const t2 = Object.create(t)
Object.getPrototypeOf(t2) // 需要注意的这里取到的是直属上级原型t。也就是{a: 'boom', b: f}
rootProto.isPrototypeOf(t2) //true 毕竟root是终极原型
t2 instanceof Test // true
t2.pb() //boom
//t2现在拥有a b pa pb四个继承属性
t2.a = 'Im t2'
//t2现在拥有自身属性a 继承属性b pa pb
哇!!我觉得对于构造函数!!我一下子开窍了!!
突然领悟到构造函数的重点不是构造函数本身,而是产生的object这个结果。这里有一句虽然现在看起来过时 但却一语中的的话“JavaScript is a prototypal object oriented language. This means that objects in JavaScript directly inherit from other objects. Hence we don't need classes. All we need is a way to create and extend objects.”构造函数起到的正是create与extend objects的作用。
它指定输入,可以输出符合某种长相的对象。它还可以随身携带一个对象作为原型老大,以后生成的对象也会把原型老大当作终极原型,不管继承多少代。
如何继承对象?
如何继承构造函数?
function A(){}
A.prototype.a = 1
function B(){}
B.prototype = Object.create(A.prototype)
B.prototype.b = 1
B.prototype.constructor = B
var b = new B()
console.log(b.a) \\ 1
console.log(b instanceof A) \\ true
console.log(b instanceof B) \\ true
恩以上是通过构造函数来继承的,实际上只要有一个对象,对象身上会携带着自己的prototype,因此别的对象也可以直接继承这个对象
function A(){}
A.prototype.a = 1
var a = new A()
function B(){}
B.prototype.b = 1
var b = new B()
b.__proto__=a 或b=Object.create(a)
console.log(b instanceof A) // true
console.log(b instanceof B) // false
----蒙了我半晌的问题!疑问:
var A = function(){this.a = 1}
var a = new A()
var B = function(){this.b = 1}
B.prototype.say = function(){console.log('hh')}
var b = new B()
A.prototype = Object.create(B.prototype)
console.log(a.say())
结果是a.say is undefined..当时我就很奇怪 不是已经“指定”了A的prototype了吗 在A的prototype上有say方法 为什么小a没有say方法呢?其实就是…一个顺序所导致的切断引用的问题呀!可以看出a.constructor.prototype===A的原始prototype,所以只可以A's original prototype.say = xx这样来事后添加新的属性。而我上述的“指定”更像是“重定向”,直接将A.prototype指向一个别的对象,但是a.constructor.prototype指的地方还是原处,就无法实现想要的结果了。
Class 类
class Test{
constructor(a) {
this.a = a
}
b(){
//this指代的是实例化对象
console.log(this.a)
}
static c(s){
//this指的是Test这个class
const b = this.b //undefined
return s
}
}
typeof Test //function
Test.c('im Test')
//c于Test是什么关系呢
const t = new Test('boom') // {a: 'boom'}
t.constructor //class Test
Object.getPrototypeOf(t) //{b: f, constructor: class Test}
Object.getOwnPropertyNames(t) //['a']
for (props in t) { } //a
'b' in t //true 说明b以不可枚举继承属性存在着
//此时t的可枚举自身属性为a 不可枚举继承属性为b
const t2 = Object.create(t)
t2.constructor //class Test
Object.getPrototypeOf(t2) //Test {a: 'boom'}即t指向的对象
for (props in t) {} //a
//此时t2的可枚举继承属性是a 不可枚举继承属性是b
class TestExtends extends Test{
constructor(a, a2){
super(a)
super.b() //打印出z
this.a2 = a2
}
b2(){
console.log(this)
}
static d(){
//this指的是class TestExtends
return 'ssss'
}
}
TestExtends.c('Im TestExtends')
TestExtends.d()
const te = new TestExtends('boom', 'zzz') // {a:'z', a2: 'zzz'}
Object.getOwnPropertyNames(t) //['a']
for (props in te) { } //a
//此时te的可枚举自身属性为a 不可枚举继承属性为b, b2
//不加constructor()时 te为{a: undefined} 表明依然带着a这个属性
//子class若有任何属性与或方法与父class同名 则会覆盖后者
关于它vs 构造函数
- First, a function created by class is labelled by a special internal property [[FunctionKind]]:"classConstructor"
- Class methods are non-enumerable. A class definition sets enumerable flag to false for all methods in the "prototype".
- Classes always use strict. All code inside the class construct is automatically in strict mode.
这里尤其想讨论原型方法这一项。这一项可以看作直接替代了构造函数.prototype.方法,因为很明显class的实例化对象t中,原型方法为t的继承属性,而不是自身属性。唯一的不同点是构造函数.protype.方法是t的可枚举属性,而class的原型方法是t的不可枚举属性。
所以我最大的困惑,构造函数.prototype.方法去了哪里,以及class.原型方法到底是什么,就彻底解决了!:D
讲到静态方法static,这一项似乎是class特有的,只能通过class来调用该方法。文档说,静态方法通常用于为一个应用程序创建工具函数。感觉就是放只准给这个类调用的函数而已…
然后还有一个超神奇的!super!我一开始只知道它起到在实例化子类时,执行父类的constructor()并给之传参的作用。其实它还可以当场调用父对象上的原型函数,所以super.b()才可以执行。
extends也可以用于扩展常规的构造函数!然后这个extends的意义正在于原型方法。因为普通构造函数的原型方法是不可扩展的,只要rootPrototype上添加一条,它的所有子类都会加上。而class则可以另开一个类,在继承原类原型方法的基础上,又能覆盖,还能新增自己的原型方法,以传给自己要生产的对象们。岂不是特别的棒!
关于React的思考。
经过对于class的整理后 我开始有了一点渐渐明了的概念啦!那就是比如底下:
其中的state肯定在Component上有定义,componentDidMount和render这些肯定也有。那么子class就借助了“覆盖”的概念,以自己定义的属性或方法覆盖了父级的方法。然后Component一定有个类似init功能的方法。在init(){}里,会用到this.state,this.componentDidMount()及this.render()等,那么就起到了直接调用到子class的方法,也就是拓展class的概念。
表述不清 希望以后的自己多多海涵呀。
p.s. 以后的自己表示 思考的很好哈哈哈!以后的以后的自己也要像这个过去的自己学习哦!