基本类型(String、Boolean、Number、null、undefined),是按值访问,能够操做保存在变量中的实际值
var name = 'jobs' console.log(name.toUpperCase()) // 输出JOBS console.log(name) // 输出jobs name.age = 20 name.say = function() {……} console.log(name.age) // undefined console.log(name.say) // undefined // 其中name的实际值并未变化,toUpperCase()方法是返回了一个新的字符串,也不能对基本类型添加属性和方法,代表了基本类型的值不可变
var name = 'jobs' var age = 20
栈区 | |
---|---|
name | age |
jobs | 20 |
栈区包含了变量的标识符和值
var a = 10 var b = a a++ console.log(a, b) // 11 10 // 基本类型在复制操做后变量是相互独立的互不影响
就是除了基本类型外的Object类型
var person = {} // 建立个控对象 --引用类型 person.name = 'jozo' person.age = 22 person.sayName = function(){console.log(person.name)} person.sayName() // 'jozo' delete person.name // 删除person对象的name属性 person.sayName() // undefined // 引用类型的值是同时保存在栈内存和堆内存中的对象
栈区内存保存变量的标识符和指向堆内存中该对象的指针也就是该对象在堆内存中的地址
var person1 = {name:'jozo'} var person2 = {name:'xiaom'} var person3 = {name:'xiaoq'}
栈区 | 堆区 | ||
---|---|---|---|
person1 | 堆内存地址1 | ——> | Object1 |
person2 | 堆内存地址2 | ——> | Object2 |
person3 | 堆内存地址3 | ——> | Object3 |
引用类型比较是引用的比较
var p1 = {} var p2 = {} console.log(p1 == p2) // false // 引用类型是按引用访问的,因此是比较它们的堆内存中地址是否相同
对象引用
var a = {} // 新建空对象 var b = a // 赋值后a和b指向同一个空对象 a.name = 'jobs' b.age = 20 console.log(a.name, a.age, b.name, b.age) // jobs 20 jobs 20 // 引用类型的复制是对象保存在栈区的堆内存的地址的复制,两个变量实际上指向同一个对象,所以任何操做都会互相影响
var p1 = { name: 'Vian' } var setName = function(obj) { obj.name = 'jobs' return obj } var res = setName(p1) console.log(p1.name) // jobs console.log(res.name) // jobs // 把p1传入setName()中,obj和p1指向同一个对象,修改obj的name值实际上是修改了它们共同指向的对象的name值
var p = { name: 'alice' } var set = function(ot) { ot = {} ot.name = 'tom' return ot } var re = set(p) console.log(p.name) // alice console.log(re.name) // tom // 在函数内部ot新定义了一个对象,obj引用了一个局部对象,外部p和内部ot指向了不一样对象,因此不会影响
var person = new Object() person.name = 'alice'
var person = { name: 'alice', 'age': 20 }
属性名也能使用字符串访问对象属性通常使用点语法,但js中也能使用方括号语法,而且可使用变量访问属性javascript
alert(person.name) // alice alert(person['name']) // alice var personName = 'name' alert(person[personName]) // alice
数组每一项均可以保存任何类型的数据,并且数组大小能够动态调整,跟随数据的添加而自动增加
var arr = new Array() // 使用Array构造函数 var arr = [] // 使用数组字面量表示法
if (arr instanceof Array) { // 对数组的操做 } // instanceof操做符问题在于,它假定单一的全局执行环境。若网页中包含不一样框架,那么实际上会有两个以上不一样的全局执行环境,从而存在两个以上不一样的Array构造函数
Array.isArray()
方法来判断if (Array.isArray(arr)) { // 对数组的操做 }
push()
: 可接受任意个参数,逐个添加到数组末尾,并返回修改后数组长度pop()
: 从数组末尾移除最后一项,减小length的值,而后返回移除的项unshift()
: 在数组前面添加任意个项,并返回新数组的长度shift()
: 移除数组第一个项,减小length的值,并返回移除的项reverse()
: 反转数组项的顺序sort()
: 默认升序排列,可接收一个比较函数做为参数,比较函数有两个参数,若第一个参数要在第二个参数以前,则返回负数,相等则返回0,若第一个参数要在第二个参数以后,则返回正数> sort()会调用每项的toString()转型方法,比较字符串,即便是数值也会转型后比较字符串,所以[0, 1, 5, 10, 15]使用这个方法会变成[0, 1, 10, 15, 5]
concat()
: 可接收数组、等做为参数,将数组合并var color1 = ['pink', 'yellow'] var color2 = color1.concat('skyblue', ['black', 'orange']) console.log(color1) // pink,yellow console.log(color2) // pink,yellow,skyblue,black,orange * `join()`: 可接受一个参数做为分隔符,将数组转换为字符串 * `slice()`: 基于当前数组建立一个新数组,接受一个或者两个参数,起始位置和结束位置,不会影响当前数组。 > 若只有一个参数,则返回从该参数位置开始到末尾全部的项 > 若两个参数则返回起始位置到结束位置之间的项,但不包括结束位置
splice()
: 主要做用向数组中插入项,始终返回一个数组,数组为原数组中删除的项,若没有则返回一个空数组,能够有三种用途- 删除:删除任意数量的项,接受2个参数:要删除的第一项的位置和要删除的个数 - 插入:在指定位置插入任意数量的项,接受3个参数:起始位置、0(要删除的项数、要插入的项),若是要插入多个项,能够再传入第四个,五个,任意个项 - 替换: 能够在任意位置插入任意项,同时删除任意项,接受3个参数:起始位置、要删除的项数、要插入的项
indexOf()
和lastIndexOf()
php
indexOf()
从头开始找,lastIndexOf()
从末尾开始找共有五个迭代方法,每一个方法都接受两个参数:要在每一项上运行的函数和(可选的)运行该函数的做用域对象——影响this的值。传入这些方法中的函数接收三个参数:数组项的值、该项在数组中的位置和数组对象自己
every()
: 对数组每一项运行给定函数,若该函数对每一项都返回true,则返回truesome()
: 对数组每一项运行给定函数,若该函数对任意一项返回true,则返回truefilter()
: 对数组每一项运行给定函数,返回该函数会返回true的项组成的数组map()
: 对数组每一项运行给定函数,返回每次函数调用结果组成的数组forEach()
: 对数组每一项运行给定函数,没有返回值以上方法都不会修改数组中的包含的值
var numbers = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] // every var a1 = numbers.every(function(item, index, array) { return item > 2 }) console.log(a1) // false // some var a2 = numbers.some(function(item, index, array) { return item > 8 }) console.log(a2) // true // filter var a3 = numbers.filter(function(item, index, array) { return item % 2 === 0 }) console.log(a3) // [2, 4, 6, 8, 0] // map var a4 = numbers.map(function(item, index, array) { return item * 2 }) console.log(a4) // [2, 6, 10, 14, 18, 4, 8, 12, 16, 0] // forEach /* 没有返回值和for循环迭代数组同样 */
reduce(function(total, currentValue [,currentIndex]) [,initialValue]): 是一种数组运算,一般用于将数组的全部成员"累积"为一个值。
- total:必选,初始值或者累积变量 - currentValue:必选,当前元素 - currentIndex:可选,当前元素的索引 - initialValue:可选,传递给函数的初始值 - 基本用法
/** * reduce(function(total, currentValue, currentIndex) [,initialValue]) 用于数组计算,一般用于将数组累积为一个值 */ /** * 参数tmp是累积变量,item为当前数组成员,index为数组下标;每次运行时item会累加到tmp,最后输出tmp */ let arr = [1, 2, 3, 4, 5, 6]; let sum = arr.reduce((tmp, item, index) => tmp + item); console.log(sum); // 21
- map是reduce的特例
/* *map是reduce的特例 累积变量的初始值能够是一个数组,例子中初始值是一个空数组,结果返回一个新的数组,等同于map操做;map操做均可以用reduce,因此map是reduce的特例 */ let handler = (newArr, x) => { newArr.push(x + 1); return newArr; } let arr2 = arr.reduce(handler, []); console.log(arr2); // [ 2, 3, 4, 5, 6, 7 ]
var arr = [1, 10, 3, 8, 5, 9, 4, 6]; /*冒泡排序*/ function pops(arr) { for (var i = 0; i < arr.length; i++) { for (var j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; } console.log(pops(arr)); /*快速排序*/ function quick(arr) { if (arr.length <= 1) { return arr; } var len = Math.floor(arr.length / 2); var aj = arr.splice(len, 1); var left = []; var right = []; for (var i = 0; i < arr.length; i++) { if (arr[i] < aj) { left.push(arr[i]); } else { right.push(arr[i]); } } return quick(left).concat(aj, quick(right)); } console.log(quick(arr));
try { tryCode - 尝试执行代码块 } catch(err) { catchCode - 捕获错误的代码块 } finally { finallyCode - 不管 try / catch 结果如何都会执行的代码块 } // 若是使用了try那么catch和finally必须选一个
经过
RegExp
类型来支持正则表达式,使用相似Perl的语法来建立正则表达式
var expression = / pattern / flags;
其中模式(pattern)能够是任何简单或复杂的正则表达式,每一个正则表达式能够有一个或者多个标志(flags),用以标明正则表达式的行为,匹配模式有如下三个标志.html
exec()
: 接受一个参数,即要使用模式的字符串前端
exec()
而言即便在模式匹配设置了全局标志(g),也只会返回第一个匹配项,在不设置时,在同一字符串上屡次调用也只会始终返回第一个匹配项的信息;而设置了,则每次调用都会继续匹配新的项test()
:接受一个字符串参数,在模式与该参数匹配的状况下返回true不然返回false函数实际上是对象,每个函数都是Function的实例,所以函数名实际上也是一个指向函数对象的指针,不会与函数绑定
解析器对函数声明和函数表达式有差异对待java
// 函数声明 console.log(sum(10, 10)) // 20 function sum(a, b) { return a + b } // 函数表达式 console.log(add(10, 10)) // 报错 var add = function(a, b) { return a + b }
在函数内部有两个特殊对象arguments和this
function factorial(num) { if (num <= 1) { return 1 } else { return num * arguments.callee(num - 1) /* arguments.callee便是factorial函数 */ } } console.log(factorial(10)) // 3628800
function a() { console.log(arguments.callee.caller) // 由于arguments.callee就是函数a,因此arguments.callee.caller等于a.caller } function b() { a() } a() // null 由于是顶层调用 b() // b函数的源代码
做用都是在特定做用域中调用函数,实际上等于设置this的值
apply()
:接收2个参数,一个是在其中运行的函数的做用域,一个是Array实例或者arguments对象call()
:接收2个参数,一个是在其中运行的函数的做用域,其他参数要直接传递给函数,也就是逐一列举function sum1(a, b) { return a + b } function sum2(a, b) { var s1 = sum1.apply(this, arguments) // arguments对象 var s2 = sum1.apply(this, [a, b]) // Array实例 console.log(s1, s2) } sum2(10, 10) //20 20 function sum3(a, b) { return sum1.call(this, a, b) // 参数逐一传入 } console.log(sum3(10, 10)) // 20
apply()
和call()
,真正的用武之地并不在于传递参数,而是扩充函数赖以运行的做用域
var color = 'blue' var o = { color: 'pink' } function sayColor() { console.log(this.color) } /* this指向了全局做用域window */ sayColor() // blue sayColor(this) // blue sayColor(window) // blue /* this指向了o */ sayColor.apply(o) // pink sayColor.call(o) // pink
bind()
: 它会建立一个函数的实例,其this的值会被绑定到传给bind函数的值var colors = 'skyblue' var c = { colors: 'orange' } function say() { console.log(this.colors) } var say2 = say.bind(c) say2() // orange
bind与apply、call不一样,不会马上执行
暂无
toFixed()
: 接受一个参数,表明保留几位小数,会四舍五入var num = 10 console.log(num.toFixed(2)) // 10.00
string包含length属性,返回字符串长度
charAt()
和charCodeAt()
:这两个方法都接收一个参数,即基于0的字符的位置.其中charAt()
以单字符字符串的形式返回给定位置的字符,而charCodeAt()
则是返回编码node
var stringTXT = 'hellow' console.log(stringTXT.charAt(3)) // l console.log(stringTXT.charCodeAt(3)) // 108
concat()
:用于将一个或多个字符串拼接起来slice()
,substr()
,substring()
:这三个方法都返回被操做字符串的子字符串,也都接受一或两个参数,第一个参数指定开始位置,第二个指定结束位置;slice和substring的第二个参数指定的是子字符串的最后一个字符后面的位置,substr的第二个参数则是指定返回字符的个数var txt = 'abcdefg' console.log(txt.slice(1, 3)) // bc console.log(txt.substring(1, 3)) // bc console.log(txt.substr(1, 3)) // bcd
indexOf()
和lastIndexOf()
:这两个方法接受2个参数,第一个为要查找的字符串,(可选)第二个表示查找起点位置的索引,都是从字符串中寻找指定的字符串,而后返回该字符串的位置,若没有则返回-1,一个从头开始找,一个从末尾开始找jquery
toLowerCase()
和toUpperCase()
:所有转换成小写,大写toLocaleLowerCase()
和toLocaleUpperCase()
:所有转换成小写,大写,这是根据特定地区来转换大小写,在不知道代码运行在哪一个语言的状况下使用match()
:接受一个参数,参数为正则表达式或者RegExp对象,返回一个数组,数组保存着每个匹配的子字符串var txt = 'abcdefgabcdefgabcdefg' var reg = /ab/gi console.log(txt.match(reg)) // ['ab', 'ab', 'ab']
search()
:接受一个参数,参数为正则表达式或者RegExp对象,从头开始匹配,返回第一个匹配项的索引,没有则返回-1replace()
:接收2个参数,字符串或者正则表达式或者RegExp对象,第二个则是用于替换的字符串或者函数;若第一个参数为字符串则只会替换第一个子字符串,要想替换全部,则必须是正则表达式,而且指定全局标志(g);若第二个参数是函数,则给这个函数传入的三个参数分别是匹配项第一个匹配项,第二个等等,最后两个参数为匹配项的位置和原始字符串split()
:将字符串以指定分隔符分割成字符串,分隔符能够是字符串也能够是正则表达式或者RegExp对象;也能够接受第二个参数,用于指定数组大小var uri = 'http://www.who.com/search?wd=123' console.log(encodeURI(uri)) // 结果: http://www.who%08.com/search?wd=123 console.log(encodeURIComponent(uri)) // 结果: http%3A%2F%2Fwww.who%08.com%2Fsearch%3Fwd%3D123
encodeURIComponent使用的比encodeURI多
decodeURI()
:解码方法,只能对encodeURI()
编码的进行解码decodeURIComponent()
:解码方法,能够解码全部字符eval()
:这个方法像是一个完整的ECMAScript解析器,它接受一个字符串参数,就是要执行的js代码字符串min()
,max()
:求最小,最大值;能够接受任意个参数ceil()
:向上求整floor()
:向下求整round()
:四舍五入random()
: 返回一个0-1之间的随机数,但不包含0和1,从区间内取一个值,能够用一下公式(max - min) * Math.random() + min
abs()
: 绝对值pow(a, n)
: 第一个为底,第二个参数为次方值,a的n次方(undefined,number,string,boolean)属于简单的值类型,不是对象。函数、数组、对象、null、构造函数都是对象,都是引用类型判断一个变量是否是对象,值类型用typeof,引用类型用instanceof
function F() { this.name = 'jobs' } var f1 = new F() // 对象能够经过函数建立 var arr = [1, 2, 3] // 语法糖,本质是下面的简写 var arr = new Array() // Array是构造函数 arr[0] = 1 arr[1] = 2 arr[2] = 3
prototype原型
[image:DF9C0DF3-83B1-4F0C-8F2B-39C112BA24F6-1188-00000B31D26AD3B2/prototype-1.png]es6
[image:C2050E95-6407-4A59-9B93-D6F7081CD863-1188-00000B3DEE188E44/instanceof-2.png]ajax
function Fn() {正则表达式
Fn.prototype.name = '王福鹏' Fn.prototype.getYear = function() { return 1988 }
}
[image:E2D062D3-B7D0-457C-97D2-6607E729A1C5-1188-00000B470B9F9F19/prototype-3.png]
function Fn() { Fn.prototype.name = '王福鹏' Fn.prototype.getYear = function() { return 1988 } } var f1 = new Fn() console.log(f1.name) // 王福鹏 console.log(f1.getYear()) // 1988 console.log(f1.__proto__ === Fn.prototype) // true
__proto__
是浏览器提供的属性,就是[[prototype]]
这个隐藏属性,__proto__
由于不是规范属性,因此要避免使用;
[[prototype]]
es6中用Object.setPrototypeOf(obj, prototype)函数能够直接修改一个对象的[[prototype]]
__proto__
,可称为隐式原型__proto__
是一个隐藏属性var obj = {} console.log(obj.__proto__ === Object.prototype)
__proto__
`属性指向建立该对象的函数的prototype属性Object.prototype.__proto__ === null
console.log(Function instanceof Function) // true console.log(Function instanceof Object) // true console.log(Object instanceof Function) // true console.log(Object instanceof Object) // true
__proto__
来找,沿着b的prototype来找,若是引用的同一个引用就返回true[image:E4783050-568F-4369-A33E-192AE586FBE7-1188-00000B9545C250D1/instanceof-1.png]
[image:8826A813-C433-43DB-9C3D-CF0964451DDA-1188-00000B97B94D72D0/instanceof-2.png]
function Foo() {} var f1 = new Foo() f1.a = 10 Foo.prototype.a = 100 Foo.prototype.b = 200 console.log(f1.a, f1.b) // 10 200
上述代码,f1是Foo函数new出来的对象,f1.a是f1对象的基本属性,而f1.b是从Foo.prototype得来的,由于
f1.__proto__
指向Foo.prototype
__proto__
链向上查找,这就是原型链[image:802EAC7F-3F88-4EA9-A120-E988C06FB7B3-1188-00000B9C76399666/prototype-4.png]
[image:7231EB7A-F0A5-482E-9632-8C88F3FA7749-1188-00000BA5D0FE85B9/prototype-5.png]
[image:B0C9A9CF-FB04-4776-A2E5-AA72796DC573-1188-00000BA936740391/this-1.png]
- 第一句报错a未定义,二三句a为undefined,说明在js代码一句句执行前,浏览器就已经开始一 些准备工做,其中就包括对变量的声明,而不是赋值,赋值操做是在运行到语句时才执行;
[image:2185D116-A4C9-41BC-BF30-92F3E57EC3B3-1188-00000BAD8E05AD77/this-2.png]
- 这是第一种状况
[image:DCC314B3-40D8-4F45-8DC1-72AB6A089648-1188-00000BB1A9090280/this-3.png]
- 第一种状况是对变量只声明,未赋值;第二种直接赋值给了this;这也是‘准备工做’之一
[image:88C73D7D-40A0-470F-81BE-B2651D40A767-1188-00000BB70805B00A/this-4.png]
- 在这里对函数表达式和普通变量赋值同样,而函数声明则马上赋值了
准备工做完成的事
js在执行代码片断前都会进行准备工做来生成‘执行上下文’,代码片断分三种状况:全局代码、函数体、eval代码
普通变量(包含函数表达式) | 声明(默认赋值为undefined) |
函数声明 | 赋值 |
this | 赋值 |
参数 | 赋值 |
arguments | 赋值 |
自由变量的取值做用域 | 赋值 |
分四种情况
function F(num) { this.num = num this.log = 'txt' console.log(this) }
- 把函数做为构造函数,那么this就是new出来的对象 - 函数做为对象的一个属性 - 若是函数做为对象的一个属性,而且做为对象的属性被调用时,this指向该对象
var obj = { x: 10, fn: function() { console.log(this) // {x:10, fn: function} console.log(this.x) // 10 } } obj.fn()
- 函数调用call和apply,bind时调用 - 当一个函数被call和apply调用时,this的值就取传入的对象的值
var obj = { x: 10 } function fn() { console.log(this) console.log(this.x) } fn.call(obj) // {x: 10} 10 fn.apply(obj) // {x: 10} 10 var fns = fn.bind(obj) fns() // {x: 10} 10
全局和调用普通函数
[image:C6407A7F-8B78-4A86-8C15-6697981F687C-1188-00000BFD4FA9B6C0/this-5.png]
[image:08320C13-AE3E-4C60-A615-25442AD209CA-1188-00000BFECEDB8EC1/this-6.png]
[image:12DFA8C1-973E-4680-A97D-C57F33ABEBCB-1188-00000C050C4391C4/this-7.png]
包含两种数据属性和访问器属性
数据属性包含一个数据值的位置,在这个位置能够读取和写入值,数据属性有4个描述其行为的特性
[[Configurable]]
:表示可否经过delete删除属性,从而从新定义属性,可否修改属性特性,可否把属性修改成访问器属性[[Enumerable]]
:可否经过for-in循环返回属性[[Writable]]
:可否修改属性值[[Value]]
:包含这个属性的数据值,默认undefined
直接在对象上定义的属性,它们的
[[Configurable]]、[[Enumerable]]、[[Writable]]的值默认为true,[[Value]]为指定值
var p = { name: 'nico' } // [[Value]]的值为 nico
要修改属性默认特性,必须使用es5的Object.defineProperty()方法,接收三个参数,属性所在对象,属性名字和一个描述符对象。其中描述符对象属性必须是:configurable、enumerable、writable、value.设置其中一个或多个值(小写)能够屡次调用Object.defineProperty(),可是Configurable一旦设置为false(不可配置),就不能再将其设置为true(可配置),并且为false时其余属性也受到限制了
在调用Object.defineProperty()时,若不设置,configurable、enumerable、writable的值都为false
var Person = {} Object.defineProperty(Person, 'name', { configurable: false, wirtable: false, value: 'alice' }) console.log(Person.name) // alice delete Person.name console.log(Person.name) // alice Person.name = 'Nico' console.log(Person.name) // alice Object.defineProperty(Person, 'name', { configurable: true, value: 'Nico' }) console.log(Person.name) // 报错
访问器属性不包含数据值,包含一对getter和setter函数(不过它们不是必须的),在读取访问器属性时,会调用getter函数,它负责返回有效的值;在写入访问器属性时会调用setter函数,它负责处理数据;访问器有以下4个特性
* [[Configurable]]
:表示可否经过delete删除属性,从而从新定义属性,可否修改属性特性,可否把属性修改成访问器属性
[[Enumerable]]
:可否经过for-in循环返回属性[[Get]]
:在读取时调用的函数,默认值为undefined[[Set]]
:在写入属性时调用的函数,默认值为undefined访问器属性不能直接定义,必须使用Object.defineProperty()来定义
var book = { _year: 2014, // _ 是一种经常使用标记,用于表示只能经过对象方法访问的属性 edition: 1 } Object.defineProperty(book, 'year', { get: function() { return this._year }, set: function(val) { if (val > 2014) { this._year = val this.edition = val - 2014 } } }) book.year = 2016 console.log(book.edition) // 2
这是使用访问器属性的常见方式,即设置一个属性的值会致使其余属性发生变化不必定要同时使用getter和setter,只指定getter表示属性不能写,尝试写入属性会被忽略;只指定setter表示不能读,不然会返回undefined;在严格模式下都会抛出错误
Object.defineProperties(),es5中定义的,能够经过描述符一次定义多个属性,接收2个对象参数,第一个是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性要一一对应
var book = {} Object.defineProperties(book, { _year: { value: 2000 }, edition: { writable: true, value: 1 }, year: { get: function() { return this._year }, set: function(val) { if (val > 2000) { this._year = val this.edition += val - 2000 } } } }) console.log(book.year) // 2000 book.year = 2017 console.log(book.edition) // 18
Object.getOwnPropertyDescriptor(),能够取得给定属性的描述符;接收2个参数:属性所在对象和要读取的其描述符的属性名,返回值为一个对象;若是是数据属性,这个对象的属性有configurable、enumerable、writable、value;若是是访问器属性,这个对象的属性有configurable、enumerbale、get、set
function createperson(name, age) { var o = {} o.name = name o.age = age o.say = function() { console.log(this.name, this.age) } return o } var p1 = createperson('alice', 18) var p2 = createperson('lulu', 20) console.log(p1 instanceof Object) // true console.log(p1 instanceof createperson) // false
工厂模式解决了建立多个类似对象的问题,但没有解决对象识别问题(怎么知道一个对象的类型)
function Person(name, age) { this.name = name this.age = age this.say = function() { console.log(this.name, this.age) } } var p1 = new Person('alice', 18) var p2 = new Person('lulu', 20) console.log(p1 instanceof Object) // true console.log(p1 instanceof Person) // true
与工厂模式对比,没有显示的建立对象,直接将属性和方法赋给了this对象,没有return语句在这个模式下建立的全部对象既是Object的实例又是Person的实例
建立自定义构造函数意味着能够将它的实例标识为一种特定类型,这正是构造函数模式赛过工厂模式的地方
使用构造函数会使得每一个方法都要在每一个实例上建立一遍,在上述例子中p1和p2都有say方法,但那不是同一个Fuction的实例,由于在js中函数是对象,会致使不一样的做用域链和标识符解析
console.log(p1.say == p2.say) // false
建立的函数都有一个prototype(原型)属性,它是一个指针,指向一个对象,这个对象的用途是包含能够由特定类型的全部实例共享的属性和方法,也就是prototype就是经过调用构造函数而建立的那个对象实例的原型对象
function Person() {} Person.prototype.name = 'nico' Person.prototype.say = function() { console.log(this.name) } var p1 = new Person() p1.say() // nico console.log(p1 instanceof Person) // true
理解原型对象
function Person() {} Person.prototype = { name: 'nico', say: function() { console.log(this.name) } } var p1 = new Person() p1.say() // nico console.log(p1 instanceof Person) // true console.log(p1.constructor == Person) // false
function Person() {} Person.prototype = { constructor: Person, name: 'nico', say: function() { console.log(this.name) } } var p1 = new Person() p1.say() // nico console.log(p1 instanceof Person) // true console.log(p1.constructor == Person) // true
但这样会致使constructor的[[Enumerable]]为true,原生的constructor为不可枚举属性,这时候要用es5的Object.defineProperty()重写constructor属性
function Person() {} Person.prototype = { constructor: Person, name: 'nico', say: function() { console.log(this.name) } } Object.defineProperty(Person.prototype, 'constructor', { enumerable: false, value: Person }) var p1 = new Person() p1.say() // nico console.log(p1 instanceof Person) // true console.log(p1.constructor == Person) // true
概述
组合使用构造函数模式和原型模式
function Person(name, age) { this.name = name this.age = age } Person.prototype = { constructor: Person, say: function() { console.log(this.name, this.age) } } var p1 = new Person('alice', 20) var p2 = new Person('nico', 30) p1.say() console.info(p1.name == p2.name) // false console.info(p1.say == p2.say) // true
function Person(name, age) { this.name = name this.age = age if (typeof this.say != 'function') { Person.prototype.say = function() { console.log(this.name, this.age) } } } var p1 = new Person('alice', 20) var p2 = new Person('nico', 30) console.info(p1.name == p2.name) // false console.info(p1.say == p2.say) // true
与工厂模式同样,instanceof操做符不能用来肯定对象类型,在能使用其余模式的状况下不推荐使用
function Person(name, age) { var o = {} o.say = function() { console.log(name) } return o } var p1 = Person('alice', 20) p1.say() // alice
这样变量Person中保存的是一个稳妥对象,除了调用say()外,没法访问其数据成员,这种模式instanceof也没什么意义
function Animal() { this.species = '猫科动物' } function Cat(name, color) { Animal.apply(this, arguments) this.name = name this.color = color } var cat1 = new Cat('kit', 'white') console.log(cat1.species) // 猫科动物
function Animal() { this.species = '猫科动物' } function Cat(name, color) { this.name = name this.color = color } Cat.prototype = new Animal() Cat.prototype.constructor = Cat var cat1 = new Cat('ly', 'blue') console.log(cat1.species) // 猫科动物
解析上述代码
Cat.prototype = new Animal()
将Cat的prototype对象指向一个Animal的实例,它至关于删除了prototype原先的值,而后赋予一个新值
- `Cat.prototype.constructor = Cat`任何一个prototype都有一个constructor属性,因为上述代码,会致使Cat.prototype.constructor指向Animal,须要从新将构造函数定义回Cat - 若是替换了prototype对象那么下一步一定是将constructor指向原来的构造函数 * 因为不能在不影响全部对象实例的状况下传参和因为原型中包含的引用类型值得问题,不多会单独使用原型链
function Animal() { this.species = '猫科动物' } function Cat(name, color) { this.name = name this.color = color } function F() {} F.prototype = new Animal() Cat.prototype = new F() var cat1 = new Cat('ly', 'blue') console.log(cat1.species) // 猫科动物 console.log(Animal.prototype.constructor) // Animal的源代码
function Animal() { this.species = '猫科动物' } function Cat(name, color) { this.name = name this.color = color } function extend(child, parent) { var F = function() {} F.prototype = new parent() child.prototype = new F() child.prototype.constructor = child child.uber = parent.prototype } extend(Cat, Animal) var cat1 = new Cat('jack', 'orange') console.log(cat1.species) // 猫科动物
这个extend函数,就是YUI库如何实现继承的方法最后一行只是为了实现继承的完备性,纯属备用性质
function creatAnother(obj) { var clone = Object(obj) clone.say = function() { console.log('hi') } return clone } var Person = { name: 'nico' } var an = creatAnother(Person) an.say() // hi
function Person(name, age) { this.name = name this.age = age } Person.prototype.sayName = function() { console.log(this.name) } Person.prototype.sayAge = function() { console.log(this.age) } function Girls(name, age) { Person.apply(this, arguments) } Girls.prototype = new Person() Girls.prototype.constructor = Girls; var alice = new Girls('alice', 16) alice.sayName() // alice alice.sayAge() // 16
function inter(Super, Sub) { var clone = Object(Super.prototype) // 建立对象 clone.constructor = Sub // 加强对象 Sub.prototype = clone // 指定对象 } function Person(name, age) { this.name = name this.age = age } Person.prototype.say = function() { console.log(this.name, this.age) } function Girls(name, age, play) { Person.apply(this, arguments) this.play = play } inter(Person, Girls) Girls.prototype.plays = function() { console.log(this.play) } var alice = new Girls('alice', 16, 'game') alice.say() // alice 16 alice.plays() // game
浏览器对象模型,提供了独立于浏览器显示内容而与浏览器窗口进行交互的对象
//假设url为 http://search.phpied.com:8080/search?p=javascript#results location.href = "http://search.phpied.com:8080/search?p=javascript#results"; location.hash = ""; location.host = "search.phpied.com:8080"; location.hostname = "search.phpied.com"; location.pathname = "/search"; location.port = "8080"; location.protocol = "http:"; location.search = "?p=javascript";
location的三个方法:
reload()
: 无参数,从新载入当前页面; location = location也能用于从新载入当前页面assign(newURL)
: 载入新页面会留下历史记录replace(newURL)
: 载入新页面不会留下历史记录window.history.lenght
: 存储的记录数history.forward()
: 前进history.back()
: 后退history.go()
: 0从新载入当前页面; 正值前进几个页面; 负值后退几个页面screen.width,screen.height
: 桌面分辨率,总大小screen.availWidth,screen.availHeight
: 除去操做系统菜单栏(例如windows的任务栏)之外的区域大小这几个并不属于ECMAScript,而是BOM方法
定时器也是BOM方法
文档对象模型,将xml或html文档解析成树形节点的方法
document.createElement()
: 建立元素节点document.createTextNode
: 建立文本节点cloneNode()
: 该方法有一个参数,true深拷贝,包括全部子节点, false浅拷贝,只针对当前节点var odv = document.getElementsByTagName('div')[0]; var p2 = document.getElementsByTagName('p')[1]; odv.appendChild(p2.cloneNode(false)); odv.appendChild(p2.cloneNode(true)); // 浅拷贝,文本节点不会拷贝
appendChild(newChild)
: 将节点插入到 节点的子节点列表的末尾insertBefore(newChild, refChild)
: 将节点插入节点指定子节点的前面var odv = document.getElementsByTagName('div')[0]; var p2 = document.getElementsByTagName('p')[1]; odv.appendChild(p2.cloneNode(true)); odv.insertBefore(p2.cloneNode(true), document.getElementsByTagName('p')[0]);
removeChild(child)
: 从子节点列表中移除指定节点replaceChild(newChild, oldChild)
: 将指定子节点替换成新节点var odv = document.getElementsByTagName('div')[0]; var p2 = document.getElementsByTagName('p')[1]; odv.replaceChild(document.createElement('li'), p2);
var forms = document.forms[0]; // var user = forms.elements[0]; 和下行同样 var user = forms.user; console.log(user); user.value = 'admin'; // <input name='user' /> 标签里有了admin
addEventListener(even, function, boolean)
: 第一个参数为事件名不加on,第二个参数为函数,第三个为布尔值默认为false在冒泡阶段执行,true在捕获阶段执行removeEventListener()
: 该方法与上一个参数相同,它是移除监听器;但如果第二个参数为匿名函数则移除不了stopPropagation()
: 这样就使得事件没法传播了,只发生在本身身上var op = document.getElementsByTagName('p'); op[0].addEventListener('click', function(e) { console.log(e.target); e.stopPropagation(); }, false);
pereventDefault()
: 不是全部默认事件都能阻止var alink = document.getElementsByTagName('a'); alink[0].addEventListener('click', function(e) { e.preventDefault(); console.log(123); }, false);
DOM2属性 | IE对应属性 | 说明 |
---|---|---|
addEventListener | attachEvent | 事件监听 |
event | window.event | IE中的全局事件对象 |
target | srcElement | 事件元素的target属性 |
stopPropagation | cancelBubble | only-IE属性,设置为true |
preventDefault | returnValue | only-IE属性,设置为false |
removeEventListener | detachEvent | 移除事件监听 |
就是HTML、CSS和JS分开
为了减小命名冲突,一般减小全局变量的使用;更好的方法是将不一样变量和方法定义在不一样命名空间中;本质是只定义一个全局变量,并将其它变量和方法定义为该变量的属性
// 新建全局变量MYAPP var MYAPP = MYAPP || {}; // 添加属性 MYAPP.event = {}; // 添加方法 MYAPP.event = { getEvent: function(e) { // ...... }, // ......other methods }
咱们能够在命名空间中使用构造器函数
// 本例中,咱们用Element构造器建立一个dom元素 var MYAPP = MYAPP || {}; MYAPP.dom = {}; MYAPP.dom.Element = function(type, props) { var tmp = document.createElement(type); for (var i in props) { tmp.setAttribute(i, props[i]); console.log(i, props[i]); } return tmp; } var a = new MYAPP.dom.Element('a', { href: "javascript.void(0);" }); document.body.appendChild(a);
var MYAPP = {}; MYAPP.namespace = function(str){ var parts = str.split("."), parent = MYAPP, i=0, l=0; if(parts[0]==="MYAPP"){ parts = parts.slice(1); } for(i=0,l=parts.length; i<l;i++){ if(typeof parent[parts[i]] === "undefined"){ parent[parts[i]] = {}; } parent = parent[parts[i]]; } return parent; }
设计模式有23种甚至更多,下面为4种经常使用模式
var sub = { callbacker: [], // 发布 add: function(fn) { this.callbacker.push(fn); }, // 订阅 fire: function(fn) { this.callbacker.forEach(function(element) { element(); }); } } sub.add(function() { console.log(1) }); sub.add(function() { console.log(2) }); sub.fire(); // 1 2
let [a, b, c] = [1, 'a', ['c']]; console.log(a, b, c); // 1, 'a', ['c']
var [a = 1, b = 2, c = 3, d = 4] = [undefined, null, 'c', d]; console.log(a, b, c, d); //1 null "c" 4
var {x, y} = {x: 1, y: 'a' }; console.log(x, y); //1 "a" var { bar: baz } = { bar: 'hello' }; console.log(baz); //hello /*下面例子中js会将{}当成代码块,此时须要用小括号括起来*/ var k; // {k} = {k: 123};会报错,js会将{}当成代码块,此时须要用小括号括起来 ({ k } = { k: 123 }); console.log(k); // 123 /*在这个例子中,loc是模式不是变量不会赋值*/ var local = { loc: { ui: 'ui', txt: 'txt' }, title: '标题' } var { loc: { ui, txt }, title } = local; // console.log(loc); loc is not defined console.log(ui, txt, title); // ui txt 标题 /*对象的解构赋值也能设置默认值,一样只有在值严格为undefined时,默认值才会有用*/ var {bat = 123, bas = 456, bad} = {bat: undefined, bas: null, bad: 'bad'}; console.log(bat, bas, bad); // 123 null "bad" /*若解构失败变量值为undefined*/ /*对象的解构赋值,能够方便的将对象方法赋值到某个变量中*/ var {pow, random} = Math; // 将Math对象的次方和随机数方法赋值给了pow和random console.log(pow(2, 3)); // 8 console.log(random()); // 随机数 /*由于数组也是特殊对象,所以数组也能进行对象的解构赋值*/ var arr = [1, 2, 3]; var {0: first, [arr.length - 1]: last} = arr; console.log(first, last); // 1 3
// 将字符串转成了相似数组的对象 const [a, b] = 'hi'; console.log(a, b); // h i // 字符串有length属性,所以能够对这个对象进行解构赋值 var { length: len } = 'hello'; console.log(len); // 5
function add([x, y]) { return x + y; } console.log(add([1, 2])) // 3 function move({ x = 0, y = 0 } = {}) { return [x, y]; } console.log(move({ x: 3 })) // [3, 0] console.log(move({ y: 8 })) // [0, 8] console.log(move({ x: 3, y: 8 })) // [3, 8]
[x, y] = [y, x];
var data = { id: 1, name: 'admin', type: 0, data: { goods: [9001, 9002], goods_type: [0, 1] } } var { id, name, type, data: { goods, goods_type } } = data; console.log(id, name, type, goods, goods_type); // 1 "admin" 0 (2) [9001, 9002] (2) [0, 1]
Array.from
Array.from方法用于将相似数组的对象和可遍历对象(包含set、map)转换成数组
var arr1 = Array(); var arr2 = Array(3); var arr3 = Array(1, 2); console.log(arr1); // [] console.log(arr2); // [ , , ] console.log(arr3); // [1, 2] var arr4 = Array.of(1); console.log(arr4); // [1] /*由于数组的构造函数,只有在参数不小于2时才会做为数组值,一个参数时做为数组长度;Array.of则是保持一致,不管参数多少都做为数组元素*/
参数:
[1, 2, 3].includes(2) // true
function add(...val) { let sum = 0; for (let i of val) { sum += i } console.log(sum); } add(1, 2, 3, 4, 5, 6, 7, 8, 9); //45
function add(...val) { let sum = 0; for (let i of val) { sum += i } console.log(sum); } var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; add(...arr); //45
var arr1 = [1, 2, 3]; var arr2 = [4, 5, 6]; // es5 Array.prototype.push.apply(arr1, arr2); // es6 // arr1.push(...arr2); console.log(arr1); // [1, 2, 3, 4, 5, 6]
// es5 var arr1 = [1, 2, 3] var arr2 = arr1.concat([4, 5, 6], 7); console.log(arr2); // [1, 2, 3, 4, 5, 6, 7] // es6 var arr3 = ['a', 'b', 'c']; var arr4 = [...arr3, ...['e', 'f', 'g'], 'h']; console.log(arr4); //["a", "b", "c", "e", "f", "g", "h"]
任何实现了Iterator接口的对象均可以使用扩展运算符转为真正的数组,对于没有Iterator接口的对象可使用Array.from
var f = () => 5; /* 就等于 var f = function() { return 5 } */ var five = f(); console.log(five); // 5 var f1 = v => v; /* 就等于 var f1 = function(v) { return v } */ console.log(f1(10)); // 10 /*代码块部分语句多余一条,那么就须要用大括号括起来,而且用return返回*/ var sum = (num1, num2) => num1 + num2; var sum2 = (num1, num2) => { return num1 + num2 }; console.log(sum(10, 20), sum2(10, 20)); // 30 30 // 若要返回一个对象,那么就要将对象用小括号括起来 var getuser = id => ({ id: id, temp: 'user' }); console.log(getuser(01)); // {id: 1, temp: "user"} /*和解构赋值相结合*/ var full = ({ user, id }) => 'id is ' + id + ', user is ' + user; var us = { id: 1, user: 'admin' }; console.log(full(us)); // id is 1, user is admin
用于取代call、apply、bind
foo::bar; // 等同于 bar.bind(foo); foo::bar(...arguments) // 等同于 bar.apply(foo, arguments);
let log = ::console.log; // 等同于 var log = console.log.bind(console);
/*es6中容许只写属性名,不写属性值;这时属性值等于属性名所表明的变量*/ var foo = 123; var baz = { foo }; console.log(baz); // {foo: 123} /*等同于 var baz = {foo: foo} */
/*es6中方法也能简写*/ var o = { say() { return 'hi!' } } /*等同于 var o = { say: function() { return 'hi!' } } */ console.log(o.say()); // hi!
var s1 = Symbol('foo'); var s2 = Symbol('foo'); var a = { [s1]: '123456', [s2]: '789456' }; console.log(a[s1], a[s2]);
ES6提供了新的数据结构Set,它相似于数组,可是成员值都是惟一的,没有重复的值
var s = new Set(); [1, 2, 2, 3, 3, 4, 5, 6].map(x => s.add(x)); console.log(...s); // 1 2 3 4 5 6 console.log(s.size); // 6
Set实例方法分为操做方法和遍历方法
set遍历顺序是按照插入顺序
js键值对中以往只能用字符串当作键;ES6提供了Map数据结构,它和对象相似也是键值对,可是它可使用任意类型的值当作键
var m = new Map(); var o = { name: 'admin', id: 1 }; m.set(o, 'content'); console.log(m.get(o)); // content console.log(m.has(o)); // true console.log(m.delete(o)); // true console.log(o.name); // admin /*能够接受数组当参数*/ var mo = new Map([ ['first', 10], [true, 'foo'] ]); console.log(mo.get('first'), mo.get(true)); // 10 foo
var mo = new Map(); var k1 = ['a']; var k2 = ['a']; mo.set(k1, 111).set(k2, 222); console.log(mo.get(k1), mo.get(k2)); // 111 222 mo.set(['b'], 333); console.log(mo.get(['b'])); // undefined // 虽然值相同可是内存地址不一样,不是同一个对象
set遍历顺序是按照插入顺序
function* test() { yield 'hello'; yield 'world'; return 'end'; } var t = test(); console.log(t.next()); // {value: "hello", done: false} console.log(t.next()); // {value: "world", done: false} console.log(t.next()); // {value: "end", done: true} console.log(t.next()); // {value: undefined, done: true} /* 调用Generator函数后, 该函数并不执行, 返回的也不是函数运行结果, 而是一个指向内部状态的指针对象, 也就是上一章介绍的遍历器对象(Iterator Object). 下一步, 必须调用遍历器对象的next方法, 使得指针移向下一个状态。 也就是说, 每次调用next方法, 内部指针就从函数头部或上一次停下来的地方开始执行, 直到遇到下一个yield语句(或return语句) 为止。 换言之, Generator函数是分段执行的, yield语句是暂停执行的标记, 而next方法能够恢复执行. */
function* foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; }; for (let v of foo()) { console.log(v); }; // 1 2 3 4 5
function* foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; }; function* ts() { yield 'a'; yield* foo(); // yield* 语句 yield 'c'; }; for (let v of ts()) { console.log(v); }; // a 1 2 3 4 5 c
let obj = { *myGenerator() { // ... } };
异步编程的最终解决方案
let show = async() => { let a1 = await $.ajax({ url: './data/arr.txt', type: 'GET', dataType: 'json' }) let j1 = await $.ajax({ url: './data/json.json', type: 'GET', dataType: 'json' }) return [a1, j1] } show().then(v => { let [a, j] = v; console.log(a, j) })
Promise是异步编程的一种解决方案, 比传统的解决方案——回调函数和事件——更合理和更强大 。Promise对象有如下两个特色:
(1) 对象的状态不受外界影响。 Promise对象表明一个异步操做, 有三种状态: Pending(进行中) 、 Resolved(已完成, 又称Fulfilled)
和Rejected(已失败) 。(2) 一旦状态改变, 就不会再变, 任什么时候候均可以获得这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种状况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会当即获得这个结果。
var test = (resolve, reject) => { var num = Math.random() * 100; setTimeout(() => { if (num > 50) { // console.log('success'); resolve('200 ok!'); } else { // console.log('error'); reject('400 fialt!'); } }, 300) } var somepromise = new Promise(test).then((result) => { console.log('成功' + result); }, (err) => { // 如有第二个函数失败后则执行这个,不执行catch console.log('err' + err); }).catch((error) => { console.log('失败' + error); }) /* catch用来指定reject的回调,效果和写在then的第二个参数里面同样; 不过它还有另一个做用:在执行resolve的回调(也就是上面then中的第一个参数)时,若是抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中; 这与咱们的try/catch语句有相同的功能. */
var p1 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('数据1'); }, 100); }) } var p2 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('数据2'); }, 100); }) } var p3 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('数据3'); }, 100); }) } p1().then((data) => { console.log(data); return p2(); }).then((data) => { console.log(data); return p3(); }).then((data) => { console.log(data); }) // 数据1 数据2 数据3
Promise的all方法提供了并行执行异步操做的能力,而且在全部异步操做执行完后才执行回调
var t1 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('t1'); }, 1000); }); } var t2 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('t2'); }, 600); }); } Promise.all([t1(), t2()]).then((data) => { console.log('所有完成'); console.log(data); }); // 所有完成 ["t1", "t2"]
竞速模式,谁先完成,以谁为准执行回调
var t1 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('t1'); }, 1000); }); } var t2 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('t2'); }, 600); }); } Promise.race([t1(), t2()]).then((data) => { console.log('竞速模式'); console.log(data); }); // 竞速模式 t2
class Student { constructor(arr) { //[name, age, sex] [this.name, this.age, this.sex] = arr; } toString() { console.log(this.name, this.age, this.sex); } } var vian = new Student(['vian', 16, 'girl']); vian.toString(); // vian 16 girl console.log(typeof Student); // function console.log(Student === Student.prototype.constructor); // true console.log(vian.toString === Student.prototype.toString); // true
constructor方法是类的默认方法,new命令生成实例时自动调用该方法,必须有,若不显式定义,则会自动添加一个空的constructor方法;默认返回实例对象(this),也能够指定返回对象
class Point { constructor([x, y]) { [this.x, this.y] = [x, y]; } toString() { console.log(...[this.x, this.y]); } } var p1 = new Point([10, 20]); var p2 = new Point([60, 80]); console.log(p1.hasOwnProperty('x')); // true console.log(p1.hasOwnProperty('y')); // true console.log(p1.hasOwnProperty('toString')); // false console.log(p1.__proto__.hasOwnProperty('toString')); // true console.log(p1.__proto__ === p2.__proto__); // true p1.__proto__.pasName = () => { console.log('Oops'); } p1.pasName(); // Oops p2.pasName(); // Oops /* x和y都是实例对象point自身的属性(由于定义在this变量上) , 因此hasOwnProperty方法返回true, 而 toString是原型对象的属性(由于定义在Point类上) , 因此hasOwnProperty方法返回false。 这些都与ES5 的行为保持一致 */
const myClass = class me { getName() { console.log(me.name); } } let t1 = new myClass(); t1.getName(); // me console.log(myClass.name); // me // console.log(me.name); // me is not defined /* 上面代码使用表达式定义了一个类。 须要注意的是, 这个类的名字是MyClass而不是Me, Me只在Class的内部代码可用, 指代当前类。 若是类的内部没用到的话, 能够省略Me, 也就是能够写成下面的形式。 const MyClass = class { //...... }; */ // 采用Class表达式, 能够写出当即执行的Class let person = new class { constructor(name) { this.name = name; } say() { console.log(this.name); } }('张三'); person.say(); // 张三
class Color { constructor([x, y, z]) { this.arr = [x, y, z]; } getColor() { console.log(...this.arr); } } class myColor extends Color { constructor([x, y, z], a) { super([x, y, z]); this.a = a; } geta() { console.log(this.a); } } var c = new myColor(['pink', 'skyblue', 'orange'], 'apple'); c.geta(); // apple c.getColor(); // pink skyblue orange
Object.getPrototypeOf()
方法能够用来从子类上获取父类 ,所以, 可使用这个方法判断, 一个类是否继承了另外一个类原生构造函数是指语言内置的构造函数, 一般用来生成数据结构。 ECMAScript的原生构造函数大体有下面这些Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()之前, 这些原生构造函数是没法继承的 ;Es6中能够继承这些
class Test { constructor(x) { this.val = x; } set pro(val) { this.val = val; } get pro() { return this.val; } } var t = new Test('测试'); console.log(t.pro); // 测试 t.pro = 123; console.log(t.pro); // 123
class Bar { static gey() { console.log(123); } } class Baz extends Bar {} Baz.gey(); // 123 // 无静态属性,只能以下添加 Baz.ps = 456; console.log(Baz.ps); // 456
<script type="module" src="foo.js"></script>
// 写法1 export var one = 1; // 写法2 var p1 = 1; var p2 = 2; export {p1, p2}; // 可使用as关键字输出别名 export {p1 as pi, p2 as po}; /* export 1; // 报错 var m = 1; export m; // 报错 上面两种写法都会报错, 由于没有提供对外的接口。第一种写法直接输出1,第二种写法经过变量m,仍是直接输出1。1只是一个值,不是接口。 */
// main.js import {firstName, lastName, year} from './profile'; function setName(element) { element.textContent = firstName + ' ' + lastName; } import { lastName as surname } from './profile';
foo(); import { foo } from 'my_module'; // 上面的代码不会报错, 由于import的执行早于foo的调用
/*circle.js文件, 它输出两个方法area和circumference*/ // circle.js export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; } /*如今, 加载这个模块*/ // 逐步加载写法 // main.js import { area, circumference } from './circle'; console.log('圆面积: ' + area(4)); console.log('圆周长: ' + circumference(14)); // 总体加载写法 import * as circle from './circle'; console.log('圆面积: ' + circle.area(4)); console.log('圆周长: ' + circle.circumference(14));
使用import命令的时候, 用户须要知道所要加载的变量名或函数名, 不然没法加载。
为了给用户提供方便, 让他们不用阅读文档就能加载模块, 就要用到export default命令, 为模块指定默认输出 。
// export-default.js export default function () { console.log('foo'); } // import-default.js import customName from './export-default'; customName(); // 'foo' /* 模块文件export-default.js, 它的默认输出是一个函数 其余模块加载该模块时, import命令能够为该匿名函数指定任意名字 上面代码的import命令, 能够用任意名称指向export-default.js输出的方法, 这时就不须要知道原模块输出的函数名。 须要注意的是, 这时import命令后面, 不使用大括号。 */
export default命令用在非匿名函数前, 也是能够的
// export-default.js export default function foo() { console.log('foo'); } // 或者写成 function foo() { console.log('foo'); } export default foo; // foo函数的函数名foo, 在模块外部是无效的。 加载的时候, 视同匿名函数加载 // 一个模块只能有一个默认输出, 所以export deault命令只能使用一次 // export default命令其实只是输出一个叫作default的变量, 因此它后面不能跟变量声明语句 // 正确 export var a = 1; // 正确 var a = 1; export default a; // 错误 export default var a = 1; // 有了export default命令, 输入模块时就很是直观了, 以输入jQuery模块为例 import $ from 'jquery'; // 若是想在一条import语句中, 同时输入默认方法和其余变量, 能够写成下面这样 import customName, { otherMethod } from './export-default'; // 若是要输出默认的值, 只需将值跟在export default以后便可 export default 42; // export default也能够用来输出类 // MyClass.js export default class { ... } // main.js import MyClass from 'MyClass'; let o = new MyClass();