精通javascript
参考文献(两个文档混合看,非常有用)
- 阮一峰 es6 :http://es6.ruanyifeng.com/#docs/object
- MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects
- webpack打包:https://segmentfault.com/a/1190000006178770#articleHeader3
- 廖雪峰的js教程:https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434499763408e24c210985d34edcabbca944b4239e20000
- over
node.js
javascript : BOM DOM ECMASCRIPT
DOM:节点和属性、增删改查、事件
HTML
CSS
框架:JQuery、Vue、Wepy、Angular
快速学习能力
DOM
- getElementById(id)
- appendChild(node)
- removeChild(node)
- innerHTML
- parentNode
- childNodes
- attributes
JQuery
jquery 安装
jquery语法
$(document).ready(function(){ --- jQuery functions go here ---- });
jquery 选择器
jquery事件
jquery 效果
jquery HTML处理
over
nodejs支持es6
'use strict'//加上这句声明
$ node test.js
let和const
{
let b = 100;
var c = 1;
}
console.log(b);//b is not defined
let命令所在的代码块内有效。
for循环的计数器,就很合适使用let命令。
var array = ['gaolong', 'xiaoming', 'xiaoli'];
for (let i = 0; i < array.length; i++) {}
console.log(i); //i is not defined, i只在for循环体内有效
var array = ['gaolong', 'xiaoming', 'xiaoli'];
var a = [];
for (let i = 0; i < array.length; i++) {
a[i] = function () {
console.log(i);
}
}
a[2](); //此处数组中存放的是函数,所以可以直接执行a[i]();重点比较let i与 var i的不同
let不存在变量提升,var存在。即脚本未执行前var变量已经存在,只是值为undefined.
let暂时性死区,temporal dead zone
var tmp = 123;
if (true) {
tmp = 'abc'; //referenceError。该区域已经被let 覆盖。
let tmp;
}
let不允许在相同作用域内,重复声明同一个变量。
因此,不能在函数内部重新声明参数。
ES5只有全局作用域和函数作用域,没有块级作用域.
因此会出现这两种情况:
第一:内层变量可能会覆盖外层变量。
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello wolrd';
}
}
f(); //undefined ,该使用let
'use strict';
if (true) {
function f() {}
}
f(); // ReferenceError: f is not defined,es6函数在块级作用域内,外部不能使用。
第二:用来计数的循环变量泄露为全局变量。
const
只读常量,不能改变。
'use strict';
const pi = 3.14159;
pi // 3.14159
pi = 3; typeerror: pi is read-only
//如果是常规模式,不会报错,但是赋值无效
//const 一旦声明就必须赋值,const foo,报错
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后使用
const声明的常量,也与let一样不可重复声明。
对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。
const foo = {}
foo.prop = 123;
foo.prop ;// 123
foo = {} //type error ;foo is readonly
const a = [];
a.push('Hello');//ok
a.length = 0; //ok
a = ["schiller"]; //error
如果真的想将对象冻结,应该使用Object.freeze方法。
全局对象属性
全局对象是最顶层的对象,在浏览器环境指的是window对象,在Node.js指的是global对象。ES5之中,全局对象的属性与全局变量是等价的。
var a = 1;
// 如果在Node的REPL环境,可以写成global.a
// 或者采用通用方法,写成this.a
window.a // 1
let b = 1;
window.b // undefined
上面代码中,全局变量a由var命令声明,所以它是全局对象的属性;全局变量b由let命令声明,所以它不是全局对象的属性,返回undefined。
解构赋值
//注意,解构赋值在node环境中暂且无法运行,具体原因待探究,可以使用新建rn项目 //跑来测试
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
var [a, b, c] = [1, 2, 3];
let [x, y, ...z] = ['a'];
// x = 'a', y = undefined, z = []
如果解构不成功,变量的值就等于undefined。
如果等号右边不是数组,严格的说不是可遍历结构,那将会报错。
//报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
上面的表达式都会报错,因为等号右边的值,要么转为对象以后不具备Iterator接口(前五个表达式),要么本身就不具备Iterator接口(最后一个表达式)
事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。
function* fibs() {
var a = 0;
var b = 1;
while (true) {
yield a;
[a, b] = [b, a+b];
}
}
var [first ,second, third, fourth, fifth, sixth] = fibs();
sixth //5
默认值
var [foo = true] = [];
foo //true
ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。
如:
[x, y = 'b'] = ['a']; // x='a', y='b'
[x, y = 'b'] = ['a', undefined]; // x='a', y='b'
var [x = 1] = [null] //x = null
对象的解构赋值
解构赋值还可以用于对象
var {foo, bar} = {foo: 'aaa', bar: 'bbb'};
// foo = 'aaa', bar = 'bbb'
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值,如:
var {foo, bar} = {bar:'aaa', foo: 'bbb'};
//foo = "bbb", bar='aaa'
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
var {foo: baz} = {foo:'aaa', bar:'bbb'}
//baz = 'aaa' //foo error, foo is not defined
//类似var { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
变量的声明和赋值是一体的
对于let和const来说,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错。
let foo;
let {foo} = {foo:1} //erro, duplicate declaration of foo
上面代码中,解构赋值的变量都会重新声明,所以报错了。不过,因为var命令允许重新声明,所以这个错误只会在使用let和const命令时出现。如果没有第二个let命令,上面的代码就不会报错。
let foo;
({foo} = {foo:1}) //ok
//允许嵌套s
var obj = {
p: [
"Hello",
{y: 'world'}
]
};
var {p:[x, {y}]} = obj;
//对象的解构也可以指定默认值,默认值生效的条件是,对象的属性值严格等于undefined
字符串的解构赋值
const [a, b,c,d, e] = 'hello';//a ='h', ...
类似数组对象都有一个length
let {length : len} = 'hello';
len // 5
数值和布尔值得解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
函数参数的解构赋值
function add([x,y]) {
return x + y;
}
var arr = [[1, 2], [3, 4]].map(([a, b]) => a + b);
函数的解构也可以使用默认值
function move ({x = 0, y = 0} = {}) {
return [x ,y];
}
move({x: 3, y: 8}); //[3,8]
move({x: 3}); //[3,0]
move({}); //[0,0]
move(); // [0,0]
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
上面代码是为函数move的参数指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。
解构赋值用途
1、交换变量的值
[x, y] = [y, x];
2、 从函数返回多个值
// 返回一个数组
function example() {
return [1, 2, 3];
}
var [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
var { foo, bar } = example();
3、 函数参数的定义
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3])
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1})
4、 提取JSON数据
var jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
}
let { id, status, data: number } = jsonData;
console.log(id, status, number)
// 42, OK, [867, 5309]
5、 函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
}) {
// ... do stuff
};
6、 遍历Map结构
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
如果只想获取键名,或者只想获取键值,可以写成下面这样。
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
7、 输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
字符串的扩展
字符的Unicde表示法
codePointAt() //如果确实要处理字符
String.fromCodePoint()
字符串的遍历器接口
at();
'abc'.at(0) // "a"
'𠮷'.at(0) // "𠮷"
normalize()
includes(), startsWidth(),endsWith()
传统上,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法:
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
var s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
//第二个参数表示搜索位置
var s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次。
es7提供补齐padStart(), padEnd()
'x'.padStart(5, 'ab').// 'ababx'
'x'.padEnd(4, 'ab').// 'xabab'
模板字符串
标签模板、实例模板:暂不讨论
String.raw();
参考文献:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Text_formatting
trim
repeat
normalize
toLowerCase, toUpperCase
match,replace,search
substring,substr
slice
split
contact
fromCharCode,fromCodePoint
startsWith,endsWith,includes
indexOf,lastIndexOf
charAt,charCodeAt,codePointAt
正则扩展
RegExp构造函数
var regex = new RegExp('xyz', 'i');
//等价
var regex = /xyz/i
或者
var regex = new RegExp(/xyz/i);
=> var regex = /xyz/i
es6:
new RegExp(/abc/ig, 'i').flags //'i'
字符串的正则方式:
字符串对象共有4个方法,可以使用正则表达式:match()、replace()、search()和split()。
正则匹配难度比较大,暂时不讨论
数值类型
二进制和八进制的表示
ES6提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。
0b111110111 === 503 // true
0o767 === 503 // true
Number.isFinite() , NumberisNaN()
ES6在Number对象上,新提供了Number.isFinite()和Number.isNaN()两个方法,用来检查Infinite和NaN这两个特殊值。
Number.isFinite()用来检查一个数值是否非无穷(infinity)。
Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false
Number.isNaN()用来检查一个值是否为NaN。
它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,非数值一律返回false。
Number.parseInt(), Number.parseFloat()
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
Number.isInteger()
Number.isInteger()用来判断一个值是否为整数。
Number.isInteger(25) // true
Number.isInteger(25.0) // true //js中25和25.0采用同样的存储方式
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false
//js中这样部署 Number.isInteger();
(function (global) {
var floor = Math.floor,
isFinite = global.isFinite;
Object.defineProperty(Number, 'isInteger', {
value: function isInteger(value) {
return typeof value === 'number' && isFinite(value) &&
value > -9007199254740992 && value < 9007199254740992 &&
floor(value) === value;
},
configurable: true,
enumerable: false,
writable: true
});
})(this);
ES6在Number对象上面,新增一个极小的常量Number.EPSILON。
Number.EPSILON
// 2.220446049250313e-16
Number.EPSILON.toFixed(20)
// '0.00000000000000022204'
//JavaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。
Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER
Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内
Math对象的扩展
Math.trunc() 、 Math.sign() 、Math.cbrt() 、Math.clz32
Math.imul()、Math.fround()、Math.hypot()、
对数方法
Math.expm1()//Math.expm1(x)返回ex - 1,
Math.log1p()、Math.log10()、Math.log2()
三角方法
指数运算符
let a = 2;
a **= 2;
// 等同于 a = a * a;
let b = 3;
b **= 3;
// 等同于 b = b * b * b;
参考文献:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Numbers_and_dates
数学对象Math
Math.PI Math.sin(1.34)
日期对象
处理日期时间的Date对象方法可分为以下几类:
"set" 方法, 用于设置Date对象的日期和时间的值。
"get" 方法,用于获取Date对象的日期和时间的值。
"to" 方法,用于返回Date对象的字符串格式的值。
parse 和UTC 方法, 用于解析Date字符串。
数字时钟
function JSClock() {
var time = new Date();
var hour = time.getHours();
var minute = time.getMinutes();
var second = time.getSeconds();
var temp = "" + ((hour > 12) ? hour - 12 : hour);
if (hour == 0)
temp = "12";
temp += ((minute < 10) ? ":0" : ":") + minute;
temp += ((second < 10) ? ":0" : ":") + second;
temp += (hour >= 12) ? " P.M." : " A.M.";
return temp;
}
数组的扩展
扩展运算符 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
复制数组
合并数组
与解构赋值结合
字符串
实现了 Iterator 接口的对象
Map 和 Set 结构,Generator 函数
Array.from() //将类似数组的对象转成真正的数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3,
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.from('hello') ;// ['h', 'e'...];
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]
Array.of方法用于将一组值,转换为数组。
Array.of(3,11,8) //[3, 11,8];
copyWithin()
数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。target、start、end三个参数
[1, 2, 3, 4, 5].copyWithin(0, 3)// [4, 5, 3, 4, 5]
find的()和findIndex//数组实例的find方法,用于找出第一个符合条件的数组成员
[1, 4, -5, 10].find((n) => n < 0)// -5
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
fill方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
['a', 'b', 'c'].fill(7, 1, 2);// ['a', 7, 'c']
数组实例的entries(), keys(), values()
参考文献:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Indexed_collections
var arr = new Array(element0, element1, ..., elementN);
var arr = Array(element0, element1, ..., elementN);
var arr = [element0, element1, ..., elementN];
//创建和填充数组
var emp = [];
emp[0] = "Casey Jones";
emp[1] = "Phil Lesh";
emp[2] = "August West";
引用数组元素
遍历数组
数组方法
contact
var myArray = new Array("1", "2", "3");
myArray = myArray.concat("a", "b", "c");
joint 将数组的所有元素连接成一个字符串。
var myArray = new Array("Wind", "Rain", "Fire");
var list = myArray.join(" - "); // list is "Wind - Rain - Fire"
push 在数组末尾添加一个或多个元素,并返回数组操作后的长度。
var myArray = new Array("1", "2");
myArray.push("3"); // myArray is now ["1", "2", "3"]
pop()
shift() 移除数组第一个元素
unshift()
slice从数组提取一个片段,并作为一个新数组返回
reverse() 颠倒数组元素的顺序:第一个变成最后一个,最后一个变成第一个。
sort() 给数组元素排序。
indexOf(searchElement[, fromIndex]) 在数组中搜索searchElement 并返回第一个匹配的索引。
lastIndexOf(searchElement[, fromIndex]) 和 indexOf 差不多,但这是从结尾开始,并且是反向搜索。
forEach(callback[, thisObject]) 在数组每个元素项上执行callback
map(callback[, thisObject]) 在数组的每个单元项上执行callback函数,并把返回包含回调函数返回值的新数组(译者注:也就是遍历数组,并通过callback对数组元素进行操作,并将所有操作结果放入数组中并返回该数组)
filter(callback[, thisObject]) 返回一个包含所有在回调函数上返回为true的元素的新数组(译者注:callback在这里担任的是过滤器的角色,当元素符合条件,过滤
器就返回true,而filter则会返回所有符合过滤条件的元素)。
every(callback[, thisObject]) 当数组中每一个元素在callback上被返回true时就返回true(译者注:同上,every其实类似filter,只不过它的功能是判断是不是数组中的所有元素都符合条件,并且返回的是布尔值)。
some(callback[, thisObject]) 只要数组中有一项在callback上被返回true,就返回true(译者注:同上,类似every,不过前者要求都符合筛选条件才返回true,后者只要有符合条件的就返回true
reduce(callback[, initialValue]) 使用回调函数 callback(firstValue, secondValue) 把数组列表计算成一个单一值(译者注:他数组元素两两递归处理的方式把数组计算成一个值)
var a = [10, 20, 30];
var total = a.reduce(function(first, second) { return first + second; }, 0);
console.log(total) // Prints 60
多维数组
var a = new Array(4);
for (i = 0; i < 4; i++) {
a[i] = new Array(4);
for (j = 0; j < 4; j++) {
a[i][j] = "[" + i + "," + j + "]";
}
}
函数的扩展
javascript中函数就是对象。对象是键值对的集合并拥有一个连接到原型对象的隐形连接,对象连接到Object.prototype,函数连接到Function.prototype.
函数是对象。
函数字面量:保留字function 、函数名(可省略,用于递归)、参数、执行域
//ES6函数默认值
var add = function(a, b = 0) {
return a+b;
}
//与解构赋值默认值结合使用
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
//函数length属性
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数
//作用域,指定函数参数
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
//rest参数
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
//name属性
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"
//箭头函数
var sum = (c, d) => {return c+d};
方法调用模式:当一个函数是一个对象的属性时
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.increment();
alert(myObject.value);// 1;
函数调用模式:当一个函数并非一个对象的属性时。
当函数以此模式调用时,this被绑定到全局对象。这是语言设计上的一个错误。正确的设计应该是当内部函数被调用时,this应该仍然绑定到外部函数的this变量。
var sum = add(3,4);
//解决的办法是:如果该方法定义一个变量并给它赋值this,那么内部函数就可以通过这个变量访问到外部函数的this.
//给myObject增加一个double方法。
myObject.double = function () {
var that = this;//如果不这么赋值会发生意想不到的错误,内部函数无法访问到外部的this,内部函数访问的是全局的this,就有bug.
var helper = function () {
that.value = add(that.value, that.value);
}
helper();//以函数形式调用helper.
};
myOjbect.double(); //myOjbect.value = 2, 否则是1.
构造器调用模式:
var Quo = function (string) {//创建一个Quo名的构造器函数。并构造一个带有status属性的对象。
this.status = string;
}
Quo.prototype.get_status = function() {//给Quo的所有实例提供一个方法。
return this.status;
};
var myQuo = new Quo("confused"); //new 构造一个Quo实例
alert(myQuo.get_status());
Apply调用模式
javascript是函数式的面向对象的语言,所以函数可以拥有方法。
apply方法让我们构建一个参数数组并用其去调用函数,apply方法接收两个参数,一个是将被绑定给this的值,第二个是参数数组。
var array = [3, 5];
var sum = add.apply(null, array);//sum = 8
var statusObject = {
status: 'A-OK',
};
var status = Quo.prototype.get_status.apply(statusObject); //status : 'A-OK'
函数参数:arguments数组是默认的。
返回值:
异常:
给函数添加方法:Function.prototype.method = function() {}
递归:
参考文献:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Functions
值传递和引用传递 ---- 函数重要知识点
function map (f, a) {
var result = [];
for (let i = 0; i < a.length; i++) {
result[i] = f(a[i]);
}
return result;
}
var f = function(x) {
return x*x*x;
}
var numbers = [0,1,2,5,10];
var cube = map(f,numbers);
console.log(cube);
for(let value in numbers) {
console.log(numbers[value]);
}
函数作用域
递归
嵌套函数和闭包
闭包
函数参数
箭头函数
this
ES6函数
箭头函数:
var f = v => v, 等同于: var f = function(v) {return v;};
var geetTmpItem = id => ({id:id, name: 'Tmp'});//返回对象时,用圆括号括起来
const full = ({first, last}) => first + '' + last;//变量解构
var sum = (num1, num2) => { return num1 + num2; }
对象
//null表示没有对象,即该处不应该有值
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
Object.getPrototypeOf(Object.prototype)// null
//undefined表示"缺少值",就是此处应该有一个值,但是还没有定义
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
var i;
i // undefined
function f(x){console.log(x)}
f() // undefined
var o = new Object();
o.p // undefined
var x = f();
x // undefined
对象有时也被叫作关联数组
var myCar = new Object();
myCar.make = "Ford"
或者
myCar["model"] = "Mustang";
//枚举属性
function showProps(obj,objName) {
var result = "";
for(var i in obj) {
if (obj.hasOwnProperty(i)) {
result += objName + "." + i + "=" + obj[i] + "\n";
}
}
return result;
}
var myCard = new Object();
myCard.make = "Ford";
myCard.model = "Mystang";
myCard.year = 1969;
console.log(showProps(myCard, "myCard"))
Object.keys(o)
Object.getOwnPropertyNames(o)
//使用对象初始化器初始化一个对象
var obj = { property_1: value_1, // property_# 可以是一个标识符...
2: value_2, // 或一个数字...
["property" +3]: value_3, // 或一个可计算的key名...
// ...,
"property n": value_n }; // 或一个字符串
//使用构造函数初始化对象
function Car(make, model, year) {
this.make = make ;
this.model = model;
this.year = year;
}
var mycard_1 = new Car("eagle", "talon",1993)
//使用Object.create方法
//为对象类型定义属性,其中Car为构造函数
Car.prototype.color = null;
car1.color = "black";
//定义方法
objectName.methodname = function_name;
var myObj = {
myMethod: function(params) {
// ...do something
}
// 或者 这样写也可以
myOtherMethod(params) {
// ...do something else
}
};
get 和 set方法
删除属性
比较对象
ES6对象方法简写
let birth = '2000/01/01';
const Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
Object.is()
Object.assign() 浅拷贝、同名属性替换、数组处理、取值函数的处理
1)给对象添加属性
class Point {
constructor(x, y) {
Object.assign(this, {x, y});
}
}
Object.assign(SomeClass.prototype, {
someMethod(arg1, arg2) {
···
},
anotherMethod() {
···
}
});
// 等同于下面的写法
SomeClass.prototype.someMethod = function (arg1, arg2) {
···
};
SomeClass.prototype.anotherMethod = function () {
···
};
3)克隆对象
4)合并多个对象
//属性的可枚举性和遍历
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
属性的遍历
//__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
// es6 的写法
const obj = {
method: function() { ... }
};
obj.__proto__ = someOtherObj;
// es5 的写法
var obj = Object.create(someOtherObj);
obj.method = function() { ... };
//super关键字
//Object.keys(),Object.values(),Object.entries()
//对象的扩展运算符
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
Class类
1.ES5写法
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return '(' + this.x + ',' + this.y + ')';
};
console.log(Point.prototype);[ 'toString' ]
2.ES6写法
class Point {
constructor(x, y) {//默认方法
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ',' + this.y + ')';
}
get prop () {//定义getter和setter函数
return 'shit-get';
}
set prop(value) {
console.log('setter'+value);
}
static classMethod() {//静态方法
return 'Hello, world';
}
}
var point = new Point(8,0);
console.log(point.toString());//(8.0)
console.log(typeof point);//object
console.log(typeof Point);//function
console.log(Point.classMethod());//调用静态方法
类的所有方法都定义在类的prototype属性上面,在类的实例上面调用方法,其实就是调用原型上的方法,如下
point.constructor = Ponit.prototype.constructor//true
3.类的内部所有定义的方法,都是不可枚举的(non-enumerable),这与ES5不一致。
console.log(Point.prototype);// Point{}
console.log(Object.getOwnPropertyNames(Point.prototype));//[ 'constructor', 'toString' ]
console.log(point.hasOwnProperty('x'));//true
console.log(point.hasOwnProperty('toString'));//false
console.log(point.__proto__.hasOwnProperty('toString'));//true
4.同ES5,类的所有实例共享一个原型对象
point1.__proto__ = point2.__proto__
可以通过__proto__为Class添加方法。
point.__proto__.printName = function(){};
//同
Point.prototype.printName = function(){};
以上更改类慎用
5.class继承
class ColorPoint extends Point{}
子类必须在constructor方法中调用super方法,否则新建实例会报错。
6.类的prototype和__proto__
(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
class A{} class B extends A{}
B.__proto__ === A;//true
B.prototype.__proto__ === A.prototype;//true
Object.getPrototypeOf方法可以用来从子类上获取父类:
Object.getPrototypeOf(B) == A//true
7.super关键字
super这个关键字,有两种用法,含义不同。
(1)作为函数调用时(即super(...args)),super代表父类的构造函数。
(2)作为对象调用时(即super.prop或super.method()),super代表父类。注意,此时super既可以引用父类实例的属性和方法,也可以引用父类的静态方法。
子类继承父类,必须调用super,且需要在this前调用
8.实例的__proto__属性
子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。
因此,通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为。
9.ES6允许原生构造函数的继承
class MyArray extends Array {
constructor(...args) {
super(...args);
}
}
但是es5是不能够直接继承原生构造函数的。
Boolean()/Number()/String()/Array()/Date()/Function()/RegExp()/Error/Object()
9.Class的getter、setter
写法见上2。
10.class的generator函数
class Foo {
constructor(...args) {
this.args = args;
}
* [Symbol.iterator]() {
for (let arg of this.args) {
yield arg;
}
}
}
for (let x of new Foo('hello', 'world')) {
console.log(x);
}
如果某个方法之前加上星号(*),就表示该方法是一个Generator函数。
Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器.
11.Class的静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。
如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
代码见上2。
父类静态方法可以被子类继承,且可以从super对象调用。
12.Class静态属性和实例属性
class Foo {
}
Foo.prop = 1;
Foo.prop // 1
只有之中方法定义可行。es6类内部不能定义静态属性。
目前,es6的实例属性也只能定义在方法或者constructor方法里。es7允许。
13.newTarget属性
14.Mixin模式
Mixin模式指的是,将多个类的接口“混入”(mix in)另一个类。它在ES6的实现如下。
class DistributedEdit extends mix(Loggable, Serializable) {
// ...
}
15.Object.getPrototypeOf方法可以用来从子类上获取父类。
Iterator和 for…of循环
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Promise对象
1.Promise含义、基本用法
var promise = new Promise(function(resolve, reject) {
console.log('fuck');
var shit = 'g';
if ('st') {
resolve('shit');
}else {
reject('go');
}
});
console.log(promise);
promise.then(function(value) {//使用、success、resolved
console.log('we did it.');
}, function(error) {//failure、rejected
});
Promise对象三种状态:Pending、Resolved、Rejected
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
console.log(Object.getOwnPropertyNames(promise.__proto__));
//[ 'constructor', 'chain', 'then', 'catch' ]
2.Promise对象实例
var getJSON = function(url) {
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
function handler() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
3.Promise.prototype.then()
console.log(Object.getOwnPropertyNames(promise.__proto__));//[ 'constructor', 'chain', 'then', 'catch' ]
then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
//...
});
面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
4.Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
getJSON("/posts.json").then(function(posts) {
//...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});//如果状态变为Rejected则会调用catch方法指定回调函数。处理错误。
5.Promise.all()
用于将多个Promise实例包装成一个新的Promise实例。只要数组中其中一个状态被reject,则返回一个reject实例。
var promises = [1,3,4,5,90,8].map(function(id) {
return getJSON("/post/" + id + ".json");
});
Promise.all(promises).then(function(post){
//...
}).catch(function(reason){
/...
});
6.Promise.race()
同上、方法
7.Promise.resolve()
该方法将现有对象转化为Promise对象。
Promise.resolve('foo')
//等价于
new Promise(resolve => resolve('foo'));
8.Promise.reject()
Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。用法与上面一致
var p = Promise.reject('出错了');
=> var p = new Promise((resolve, reject) => reject('出错了'));
p.then(null, function(s) {
console.log(s);
});//出错了
9.done()和finally()
Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误.
10.应用
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
var image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
async函数与Promise、Generator函数一样,是用来取代回调函数、解决异步操作的一种方法。它本质上是Generator函数的语法糖。async函数并不属于ES6,而是被列入了ES7,但是traceur、Babel.js、regenerator等转码器已经支持这个功能,转码后立刻就能使用。
Set和Map结构
1.Set,不会重复添加元素
var s = new Set();
[1,2,1,5,5,8,6,2,7].map(x=>s.add(x));
for(let i of s){log(i)}//1,2,5,6,7,8
2.常用操作
var set = new Set([1,2,3,4,4]);
[...set];//[1,2,3,4]
set.size; 4
[...new Set(array)];//去除数组重复成员
向Set加入值的时候,不会发生类型转换,5和'5'不同,NaN等于自身
两个对象总是不等:set.add({}); set.size =1,set.add({}),set.size=2;
2.属性和方法
属性:constructor、size
方法:操作方法和遍历方法
get(key);
set(key,value);
add(value)
delete(value)
has(value)
clear()
Array.from(set)=>array
keys();
values();
entries();
forEach();set.forEach((value, key)=>console.log(value*2));
扩展运算符内部用for...of循环,所以也可以用于Set结构,数组的map和filter方法也可以用于Set了。
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// [1, 2, 3, 4]
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// [2, 3]
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// [1]
3.WeakSet
4.Map
JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但是只能用字符串当作键。这给它的使用带来了很大的限制.
Map也是键值对,但是键的范围不限于字符串,各种类型的值都可以当做对象。
var m = new Map();
var o = {p: 'Hello world'};
m.set(o, 'content');
m.get(o);//"content"
m.has(o)//true
m.delete(o)//true
m.has(o)//false
var map = new Map([['name', '张三'], ['title', 'Author']]);//接受数组做参数
3.与其他类型的互相转化
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
[...myMap];// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
转为对象:
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
Module
1.前言
JavaScript语言一直没有分模块的东西,社区解决方案是CommonJS和AMD两种。
静态优化、运行时加载。
es5、CommonJS模块
let {stat, exits, readFile } = require('fs');
上述代码中实质上是整体加载fs模块生成一个对象,然后从对象中读取三个方法。为运行时加载。
es6、Module
import {stat, exits, readFile} from 'fs';
上述代码的实质是从fs中加载3个方法,其他方法不加载,为编译时加载。
2.use strict
严格模式主要有以下限制。
变量必须声明后再使用
函数的参数不能有同名属性,否则报错
不能使用with语句
不能对只读属性赋值,否则报错
不能使用前缀0表示八进制数,否则报错
不能删除不可删除的属性,否则报错
不能删除变量delete prop,会报错,只能删除属性delete global[prop]
eval不会在它的外层作用域引入变量
eval和arguments不能被重新赋值
arguments不会自动反映函数参数的变化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局对象
不能使用fn.caller和fn.arguments获取函数调用的堆栈
增加了保留字(比如protected、static和interface)
3.export命令
export 可输出函数、类、变量
export var firstName = 'Mike';
export default Home;
export {firstName, lastName, year};
export function mutiply(x, y) {
return x * y;
}
export {f};//f为function
通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。
function v1(){...}
export {v1 as streanV1};
另外,export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);上面代码输出变量foo,值为bar,500毫秒之后变成baz
4.import
import {firstName, lastName, year} from './profile';
import {lastName as surname} from './profile';//使用as关键字重命名
5.模块的整体加载
//circel.js
export function area(){}
export function name(){}
//main.js
import {area, name} from './circle'
整体加载:使用 * as关键字
import * as circle from './circle'
console.log(circle.area);
6.export default命令
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
export default function() {log('foo')}; //export_default.js
import customname from './export_default'
customName();//'foo'
7.模块的继承
Generator函数
1.属性
Generator函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。
执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
function* helloworldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloworldGenerator();
hw.next();//hello,done
hw.next();//world;
hw.next();//ending
hw.next();//undefined
2.for...of循环
for(let v of helloworldGenerator()) {
console.log(v);//1 2 3 4 5
}
webpack入门
参考文献:https://segmentfault.com/a/1190000006178770#articleHeader3c 1、webpack作用 WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用 2、webpack安装 //全局安装 npm install -g webpack //安装到你的项目目录 npm install --save-dev webpack 3、正式使用 在项目根目录创建package.json 文件 或者 在终端通过npm init创建pakage.json 4、创建app和public文件夹 5、正式使用webpack # {extry file}出填写入口文件的路径,本文中就是上述main.js的路径, # {destination for bundled file}处填写打包文件的存放路径 # 填写路径的时候不用添加{} webpack {entry file} {destination for bundled file} # webpack非全局安装的情况 node_modules/.bin/webpack app/main.js public/bundle.js 6、通过配置文件使用webpack 在项目根目录下创建webpack.config.js module.exports = { entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js"//打包后输出文件的文件名 } } 7、更快捷打包方式 { "name": "lib", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack", "server": "webpack-dev-server --open" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.11.0" } } 8、使用webpack构建本地服务器 全局安装 npm install -g webpack-dev-server 在终端输入npm run server 即可打开浏览器查看结果 9、Loaders 10、Babel安装和配置 # npm一次性安装多个依赖模块,模块之间用空格隔开.非全局安装 npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react #npm install --save react react-dom npm install --save react react-dom Babel其实可以完全在 webpack.config.js 中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。我们现在的babel的配置并不算复杂,不过之后我们会再加一些东西,因此现在我们就提取出相关部分,分两个配置文件进行配置(webpack会自动调用.babelrc里的babel配置选项) 11、一切皆模块 webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似 @import 和 url(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。 12、CSS module 被称为CSS modules的技术意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack对CSS模块化提供了非常好的支持,只需要在CSS loader中进行简单配置即可,然后就可以直接把CSS的类名传递到组件的代码中,这样做有效避免了全局污染 module.exports = { ... module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true, // 指定启用css modules localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式 } } ] } ] } };