复合表达式和空语句javascript
复合表达式意思是把多条表达式链接在一块儿造成一个表达式java
{
let a = 100; let b = 200; let c = a + b; }
注意这里不能再块级后面加分号,块内都有缩进不是必须的,块内的原始语句经过;号进行分割python
空语句在块级或者()后面加分号表示为空语句,能够理解为何都没有的语句,在语句后面加分好";"shell
就算要写空语句最好也在后面加上注释编程
for(let i = 0, i < a.lenght; a[i++] = 0) /* empty */ ;
声明语句json
变量声明:let和const不须要说了数组
函数声明浏览器
函数声明的语法以下:app
function funcname([arg1, [arg2, ....]]) { statements }
函数的定义也能够写成以下:函数式编程
let f function(x) {return x +1}
条件语句
和Python的条件判断基本类似
if(表达式为真){ 为真的时候执行的语句 }
何时为假:nul、undefind、false、0、NaN
else和 else if
let n = 100; if(n === 1){ //执行代码块1 }else if (n === 2){ //执行代码块2 }else if (n === 3){ //执行代码块3 }else{ //若是上面的都没有执行执行这里 }
switch语句感受和shell中的case语句很像,很是适合这种多匹配的表达式
switch (n){ case 1: // 若是n === 1 从这里执行 break; // 执行完后跳出switch语句 case 2: // 若是n === 2 从这里执行 break; // 执行完后跳出switch语句 case 3: // 若是n === 3 从这里执行 break; // 执行完后跳出switch语句 default: // 若是上面的都没有匹配执行这里 break }
下面的例子更贴近实战
// switch实战 function convert(x) { switch (typeof x){ case 'number': // 若是是数将它转换为16进制 return x.toString(16); case 'string': // 若是是字符串 return '"' + x + '"'; default: // 使用普通方法转换其余类型 return String(x) } }
循环
while循环和python中的同样
n = 0; while (n < 10){ // 当条件不成立的时候中止循环不然会一直执行 //要执行的语句 console.log(n); n++ }
JavaScript中还有一个很是有意思的do while至少执行一次的while循环它不像while这么经常使用,由于这种状况少~~!
let n1 = 0; do { // 这里是while的执行内容 console.log('执行一次'); console.log(n1++) }while (n1++ < 10); // 这里是条件结果,当条件为假的时候中止
for循环有些特殊,有两种for循环一个一个看先看第一种与while循环相似的
for(let i = 0; i < 10; i){ // (初始值; 中止条件; 初始值操做 能够理解用来达成中止条件的操做) console.log(i); // 循环内容操做 console.log('Hello World') }
它很适合用在循环数组对象的地方
let loopArray = ['a', 'b', 'c', 'd', 'e']; for(let i = 0; i < loopArray.length; i++){ // 这个循环中咱们能够看出来这个i 初始值为0,能够理解为数组的下标0开始循环当小于数组长度的时候中止 // 循环一次自加1达到了循环的效果 console.log(loopArray[i]) }
当循环普通对象的时候又有一个新的循环for / in 相似python中的循环,很呛人~~
在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的。可枚举性决定了这个属性可否被for…in查找遍历到。若是一个对象中的属性是可枚举的那么它们枚举的顺序根据各自的浏览器可能不一样知道就能够了
正常的循环对象或者数组对象和Python差很少:
function Person(name) { this.name = name } Person.prototype = { age : 18, job : 'shuaige' }; let shuaige = new Person('shuai'); for(let i in shuaige){ console.log(i) }
break、continue、return 没必要说了
异常处理
和Python中的异常处理差很少颇有意思
// 异常处理 try{ // 这里若是没有异常执行到尾的操做 }catch(e){ // 这里是若是出现异常以后执行的操做 }finally { // 这里是无论你是否只是成功仍是失败都执行的操做 }
功能和Python异常同样,都是能够表面异常后致使程序终止的地方均可能须要加上异常处理,看本身程序的设计了
终于到对象了,弱弱的问下你有对象吗?
言归正传,MD JavaScript中的普通对象能够理解为Python中的字典,OK只是能够理解为Python中的字典,可是有区别的(感受是Python中字典和类的混合体~~忽略这一句话)
建立对象很简单
一、能够直接经过Object.create()建立对象
二、或者经过直接建立let a = {} 也建立了一个对象
三、或者经过new建立对象 let a = new Object() / new Date / new Array() / new RegExp()
原型
每一个JavaScript对象除了null都和另外一个对象相关联,“另外一个”对象就是咱们熟知的“原型”,
全部经过对象直接建立的对象都具备一个原型对象,并能够经过JavaScript代码Object.prototype得到原型对象的引用,举个例子:
经过new Object()建立的对象也继承了Object.prototype,一样new Date() 建立的对象也继承了Date.prototype
原型的诞生:
Brendan Eich思考以后,决定借鉴C++和java的new命令,将new命令引入了JavaScript,在传统的面向对象的语言里,new 用来构造实例对象,new 会调用构造函数,可是传统面向对象的语言new 后面的是类,内部机制是调用构造函数(constructor),而Brendan Eich简化了这个操做,在JavaScript里面,new 后面直接是构造函数,如是咱们能够这么写一个Person类:
function Person(name) { this.name = name } let personOne = new Person("Brendan Eich"); console.log(personOne.name)
这样就建立了一个新的实例了。可是new有缺陷。用构造函数生成实例对象是没法没法共享属性和方法,例以下面代码:
function Person(name) { this.name = name; this.nation = 'USA'; } let person1 = new Person("Brendan Eich"); let preson2 = new Person("Shuai Ge"); person1.nation = "China"; console.log(person1.nation); // China console.log(preson2.nation); // USA
每个实例对象,都有本身的属性和方法的副本。这不只没法作到数据共享,也是极大的资源浪费。和JavaScript工厂模式的缺点同样,过多重复的对象会使得浏览器速度缓慢,形成资源的极大的浪费。
考虑到这一点,Brendan Eich决定为构造函数设置一个prototype属性,这个属性都是指向一个prototype对象。下面一句话很重要:全部实例对象须要共享的属性和方法,都放在这个Prototype对象(原型对象)里面;那些不须要共享的属性和方法,就放在构造函数里面。
实例对象一旦建立,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分红两种,一种是本地的,另外一种是引用的。如是咱们能够改写下上面的程序:
function Person(name) { this.name = name; } // 咱们不须要指定prototype对象当咱们建立对象的时候默认会生成 Person.prototype = {nation: "USA"}; let person1 = new Person("Brendan Eich"); let person2 = new Person("Shuai Ge"); console.log(person1.nation); console.log(person2.nation);
当咱们这样写程序时候Person.prototype.nation = 'China'; 全部实例化的类的nation都会变成China。
因为全部的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象同样。prototype只是提供了实现JavaScript继承的一个很方便的途径和手段。
原型链
知道原型的做用和原型的由来后,咱们在看下原型链
function Person(name) { this.name = name } Person.prototype = { 'key1': 'value1', 'key2': 'value2' }; let person1 = new Person('shuaige'); console.log(person1.__proto__ == Person.prototype); // true console.log(person1.constructor); // [Function: Object]
如上图所示,咱们经过prototype指向了一个共享的属性,在实例化的对象中person1和person2中他们都有一个__proto__属性指向了,prototype
在prototype中又有一个constructor的属性它指向了构造函数
因为全部的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象同样。prototype只是提供了实现JavaScript继承的一个很方便的途径和手段。
能够说,由prototype、constructor、__proto__三个指针构成了整个javascript的原型链。
能够看出,原型的属性是共享的,所以,constructor属性也是共享的,能够经过实例访问
可是,另外一方面,没法经过实例修改原型中的值。若是在实例中添加了一个属性,而且属性名跟原型中的相同,那么将会在实例中建立该属性并屏蔽原型中的那个属性。能够经过delete 删除本身的属性
function Person(name) { this.name = name } Person.prototype = { constructor: Person, 'key1': 'value1', 'key2': 'value2' }; let person1 = new Person('shuaige'); console.log(person1.key1); person1.key1 = 'shuaige'; console.log(person1.key1); delete person1.key1; console.log(person1.key1); 结果: /* value1 shuaige value1 */
in 操做符及 hasOwnProperty()方法,看下面的检查属性
javascript中检测对象属性有两种方式,一种是使用in操做符,一种是hasOwnProperty()方法。这两种方式的区别是:in操做符会同时检测原型和实例,而hasOwnProperty方法只会检测实例。看下面的例子:
function Person(name) { this.name = name } Person.prototype = { constructor: Person, 'key1': 'value1', 'key2': 'value2' }; let person1 = new Person('shuaige'); // 判断属性中是否有name,取值范围是实属性和原型属性 console.log("name" in person1); console.log("key1" in person1); // 判断name是不是实例属性,不会判断原型属性 console.log(person1.hasOwnProperty("name"));
原型的重写
因为为原型一个个单独添加属性和方法不利于封装,所以更好的作法是用封装好的属性和方法的字面量来重写整个原型对象。以下:
function Person(name) { this.name = name } Person.prototype = { constructor: Person, 'key1': 'value1', 'key2': 'value2' };
注意constructor 的属性,若是不在新的原型对象中从新设定,那么constructor属性将不在指向Person。由于这里彻底重写了默认的prototype对象,所以constructor属性将指向新的contructor—object构造函数。
可是,若是使用重写原型的方法,就至关于切断了构造函数与最初原型的联系。一个实例只拥有一个惟一的原型,而实例中的指针又只指向原型,而不是构造函数。所以,原型的重写最好在实例以前,不然实例将指向最初的原型。以下例子:
function Person() { } let person1 = new Person(); Person.prototype = { constructor: Person, 'name': 'dashuaige', 'key1': 'value1', 'key2': 'value2' }; console.log(person1.name); // undefined console.log(person1.constructor.prototype.name); // dashuaige
属性的查询
function Person() { } Person.prototype = { constructor: Person, 'name': 'dashuaige', 'key1': 'value1', 'key2': 'value2' }; let person1 = new Person(); console.log(person1.name); // 获取name属性 console.log(person1["name"]); // 获取name属性
经过.访问的时候相似C和java对象的静态字段访问相似
第二种方法和python中的字典相似
删除属性
经过delete进行删除
function Person() { } Person.prototype = { constructor: Person, 'name': 'dashuaige', 'key1': 'value1', 'key2': 'value2' }; let person1 = new Person(); person1.age = 18; //设置一个属性age = 18 person1['hobby'] = 'belle'; //设置一个属性hobby = belle delete person1.age; delete person1.hobby;
检查属性
JavaScript对象能够看作属性的集合,能够经过in、hasOwnProperty、propertyIsEnumerable来完成查询
in
in左侧是属性名(字符串)右侧是对象,若是对象自有属性或继承属性中包含这个属性返回true
hasOwnProperty
hasOwnProperty检测给定的名字(字符串)是不是对象的自有属性,若是是对象的自有属性返回true若是是原型属性返回false
function Person() { } Person.prototype = { constructor: Person, 'name': 'dashuaige', 'key1': 'value1', 'key2': 'value2' }; let person1 = new Person(); person1.age = 18; //设置一个属性age = 18 person1['hobby'] = 'belle'; //设置一个属性hobby = belle console.log("hobby" in person1); // true 判断给定的左侧名字是否在自有属性或原型属性中 console.log(person1.hasOwnProperty("hobby")); //若是是自有属性返回 true console.log(person1.hasOwnProperty("name")); //若是是原型属性返回 false
枚举属性
let o = {'x': 1, 'y': 2, 'z': 3}; o.propertyIsEnumerable("toString"); // 设置为不可枚举 for(p in o) console.log(p)
为何要有这个枚举属性
有许多工具库给Object.prototype添加了新的方法或属性,这些方法和属性能够被全部的对象继承并使用,然而在ECMAScript5标准前,这些方法是不能定义为不可枚举的。
所以他们均可以被for/in 循环中枚举出来。为了不这种状况须要过滤for/in循环中的属性,下面两种方式是最多见的
for(let i in person1){ if (!person1.hasOwnProperty(i)) continue; // 跳过继承属性 console.log(i) } for(let i in person1){ if(typeof person1[i] == "function") continue; // 跳过方法 }
getter和setter
首先明确一下他是一个对象存储属性,set 、 get ,一个只有只读方法一个只有只写方法
let o = { // 普通的数据属性 "name": "None", // 存取器属性都是成对定义的函数 get accessor_prop(){/* 这里是函数体*/}, set accessor_prop(value){/* 这里是函数体 */} };
实例
let sex = { "sex": "man" , get accessor_sex() { return this.sex }, set accessor_sex(value){ return this.sex = value}, }; // 查询 console.log(sex.accessor_sex); // 设置 console.log(sex.accessor_sex = "woman");
或许你会以为这是画蛇添足的,由于咱们彻底能够忽视get和set,直接让sex方法具有两种权限。 但之因此咱们将get和set单独拿出来,是为了更加清晰地理解ECMAScript5对javascript对象键值操做中,一个更为严谨的诠释。
类属性
想得到对象的类能够经过toString()方法,经过直接量或者new建立或者自定义的函数建立的对象的类属性都是Object,所以对自定义类来讲,没有办法经过类属性区分对象类
可扩展属性
对象的可扩展属性表示对象是否能够添加新属性,全部内置对象属性都是显示可扩展的,能够经过
function Person() { } Person.prototype = { constructor: Person, 'name': 'dashuaige', 'key1': 'value1', 'key2': 'value2' }; let person1 = new Person(); console.log(Object.isExtensible(person1)); // true 判断是否为可扩展对象 console.log(Object.preventExtensions(person1)); console.log(Object.isExtensible(person1)); // false person1.test = "valueTest"; console.log(person1); // 属性没法添加成功
序列化和反序列化
JavaScript提供了一个全局的对象JSON
let testObject = {"key1": "value1", "array1": [1, 2, 3, 4, 5]}; // 定义一个对象 let testObjectDumps = JSON.stringify(testObject); // 序列化为json格式 console.log(typeof testObjectDumps, "|", testObjectDumps); // string | {"key1":"value1","array1":[1,2,3,4,5]} let testObjectLoad = JSON.parse(testObjectDumps); console.log(typeof testObjectLoad, "|", testObjectLoad); // object | { key1: 'value1', array1: [ 1, 2, 3, 4, 5 ] }
和Python差很少可是有些特殊的地方好比
稀疏数组
// 稀疏数组 let a = new Array(5); // 建立一个数组对象,长度为5,可是没有元素,我擦擦擦~ let b = []; // 建立一个空数组,长度为0 a[1000] = 0; // 赋值添加元素,数组长度为10001 let a1 = [,]; // 次数组没有元素长度为1 let a2 = [undefined]; // 次数组包含一个值为undefined console.log(0 in a1); // false: a1在下标为0的索引处没有元素 console.log(0 in a2); // true : a2在下标为0的索引处元素
数组长度能够修改元素我擦
console.log([].length); // 输出长度为0 console.log([1, 2, 3].length); //输出长度为3 let a = [1, 2, 3, 4, 5]; console.log(a.length); // 输出长度为3 // 见证奇迹的时候到了 a.length = 3; console.log(a); // [ 1, 2, 3 ] 居然是截取保留前3个值 a.length = 0 ; console.log(a); // [] 删除全部的元素 a.length = 5 ; console.log(a); // [ <5 empty items> ] 长度为5可是没有元素,就像new Array()
数组添加元素删除元素
let a = [1, 2, 3, 4]; // 定义一个新数组 a[0] = 0; console.log(a); // 若是元素存在会被替换[ 0, 2, 3, 4 ] 可是若是是空的这也是一个增长元素的方法 a.push(5); console.log(a); // 在数组对象后面追加元素[ 0, 2, 3, 4, 5 ] 亦能够追加多个元素
删除就简单了
能够经过length。。。 也能够经过delete删除指定元素
数组方法
join 转化为字符串并拼接
let a = [1, 2, 3, 4]; console.log(a.join("-")); // 结果1-2-3-4
reverse()翻转数组方法
let a = [1 ,2, 3, 4, 5]; console.log(a.reverse()); // [ 5, 4, 3, 2, 1 ]
sort()排序方法
默认根据字母表排序若是有undefined将放到最后面
concat() 扩展数组
这个连接的是元素而不是数组自己,这个扩展数不会递归扁平化数组,也不会修改数组
let a = [1, 2, 3]; console.log(a.concat(4 ,5)); // [ 1, 2, 3, 4, 5 ] console.log(a.concat([4, 5])); // [ 1, 2, 3, 4, 5 ] console.log(a.concat([4, 5], [6, 7])); // [ 1, 2, 3, 4, 5, 6, 7 ] console.log(a.concat(4, [5, [6, 7]])); // [ 1, 2, 3, 4, 5, [ 6, 7 ] ]
slice()数组切片返回指定数组
// slice()切片方法 let a = [1, 2, 3, 4, 5]; console.log(a.slice(0,3)); // 返回[ 1, 2, 3 ] console.log(a.slice(3)); // 返回 [ 4, 5 ] console.log(a.slice(1, -1)); // 返回 [ 2, 3, 4 ] console.log(a.slice(-3, -2)); // 返回 [ 3 ]
splice()方法也是删除插入元素的方法可是和slice和concat有本质区别是它会修改调用的数组
// splice // splice 第一个参数指定了插入和删除的起始位置,第二个参数指定了应该从数组中删除多少个元素 let a = [1, 2, 3, 4, 5, 6, 7, 8,]; // 若是第二个元素起始点到后面的元素都被删除 console.log(a.splice(4), a); // 返回[ 5, 6, 7, 8 ] a被修改成[ 1, 2, 3, 4 ] // 如今a是 [1, 2, 3, 4] console.log(a.splice(1, 2), a); // 从1开始删两个是[ 2, 3 ] a被修改成[1, 4] // splice 前两个指定了起始位置和删除元素的个数,后面跟着的是插入的元素插入的位置是前面定义的位置 let b = [1, 2, 3, 4,]; console.log(b.splice(2, 0, 'a', 'b'), b); // [] 从第二个元素开始插入[ 1, 2, 'a', 'b', 3, 4 ]
push和pop方法
push和pop方法容许将数组当作栈来使用,push方法在数组尾部添加一个或多个元素,并返回新的长度,pop相反;它删除数组最后一个元素,减小数组长度并返回它删除的值,组合push和pop可以实现JavaScript数组实现先进先出的栈
// push() pop() let stack = []; console.log(stack.push(1, 2), stack); // 长度为2 [ 1, 2 ] console.log(stack.push(3), stack); // 长度为3 [ 1, 2, 3 ] console.log(stack.push([11, 22, 33,]), stack); // 长度为4 [ 1, 2, 3, [ 11, 22, 33 ] ] console.log(stack.pop(), stack); // 取出最后一个元素[ 11, 22, 33 ] 当前stack 为: [ 1, 2, 3 ]
unshift和shift
unshift和shift 与push和pop相似只是push和pop在尾部操做unshift和shift在头部操做
let stack = []; console.log(stack.unshift(1, 2), stack); // 长度为2 [ 1, 2 ] console.log(stack.unshift(3), stack); // 长度为3 [ 3, 1, 2 ] console.log(stack.unshift([11, 22, 33,]), stack); // 长度为4 [ [ 11, 22, 33 ], 3, 1, 2 ] console.log(stack.shift(), stack); // 取出最前一个元素[ 11, 22, 33 ] 当前stack为: [ 3, 1, 2 ]
ECMAScript5中数组方法
forEach方法从头至尾遍历数组,为每一个元素调用指定函数
let stack = [1, 2, 3, 4, 5, 6]; let sum = 0; stack.forEach(function (value) {sum += value}); // sum 累加数组元素里面的值 console.log(sum); // 输出sum的值 // forEach()使用三个参数: 数组元素/元素的索引/数组自己 stack.forEach(function (v, i, a) {a[i] = v + 1}); console.log(stack);
注意forEach没法在全部元素都传递给调用的函数以前终止遍历,简而言之就是活没干完不准停,若是中途想中止须要放在一个异常处理里
map()方法将调用的数组每一个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值
// map() let testArrayA = [1, 2, 3, 4, 5]; let testArrayB = testArrayA.map(function (value) { return value * value }); console.log(testArrayB); // 输出结果是:[ 1, 4, 9, 16, 25 ]
filter()筛选方法
// filter let testArray = [1, 2, 3, 4, 5]; console.log(testArray.filter(function (value){return value < 3})); // [ 1, 2 ] console.log(testArray.filter(function (value, i){return i%2===0})); // [ 1, 3, 5 ] // 也可使用它来排除空元素 let testArrayA = [1, 2, 3, 4, null, undefined, 5, 6]; console.log(testArrayA.filter(function (value) { return value !== null && value !== undefined })); // [ 1, 2, 3, 4, 5, 6 ]
every()和some()判断数组元素
every()和some()方法是数组的逻辑断定,它们对数组元素应用指定的函数进行断定,返回true或false
every针对全部当全部元素知足条件为true,任意一个不知足返回false, some与every想法只要有一个知足便可
// every() some() let a = [1, 2, 3, 4, 5, 6, ]; console.log(a.every(function (value){return value > 10})); // false 判断条件为a数组对象中全部元素都大于10 console.log(a.every(function (value){return value > 0})); // true 判断条件为a数组对象中全部蒜素都大于0 console.log(a.some(function (value) { return value > 3})); // true 判断条件为a数组中有对象大于3 console.log(a.some(function (value) { return value > 7})); // false 判断条件为a数组中有对象大于7 一个都没有返回false
reduce()和reduceRight() 指定的函数将数组元素进行组合
这在函数式编程中是常见的操做,也能够称为“注入”和“折叠”
// reduce 须要两个参数,第一个是执行简化操做的函数。第二个(可选)的参数是一个传递给函数的起始值 let a = [1, 2, 3, 4, 5,]; console.log(a.reduce(function (x, y){return x+y}, 0)); // 数组求和 15 console.log(a.reduce(function (x, y){return x*y}, 1)); // 数组求和 120 console.log(a.reduce(function (x, y){return (x>y)?x:y;},)); // 数组求最大值 5 /* reduce() 使用的函数与forEach()和map()使用不一样,若是指定第二个参数的状况下也就是其市值状况下 (x, y)的值x 是每次操做后的累加值, y 就是第一个元素的值 拿第一个举例 console.log(a.reduce(function (x, y){return x+y}, 0)); 第一次操做: x = 0 , y = 1 相加结果为1 第二次操做: x = 1 , y = 2 这里须要注意的是此次 x 为上一次的结果, y 为第二个元素依次累加 若是没有起始值,默认的x为第一个元素,y 为第二个元素 */
reduceRight() 和reduce操做同样知识如今开始顺序从右到左
indexOf()和lastindexOf()
获取元素索引,indexOf从左边开始查找计算索引值,lastindexOf从右边开始查找计算索引值
let a = [0, 1, 2, 1, 0]; console.log(a.indexOf(1)); // 从左边开始找元素值为1的下标为: 1 console.log(a.lastIndexOf(1)); // 从右边开始找元素值为1的下标为: 3 // 无论从哪一个方向找第一次出现的元素下标顺序一直是从左到右 console.log(a.indexOf(3)); // 从左边开始查找元素为3的小标为:值不存在返回-1
函数定义
函数名称、圆括号(参数)、花括号{实际执行的操做}
若是函数挂载在一个对象上,做为对象的属性,它就称之为对象方法。
参数:形参(parameter)和实参(argument),形参至关函数中定义的变量,实参是在运行时的函数调用时传入的参数
函数调用
凡是没有形参的均可以省略圆括号
let a = new Object(); let a = new Object;
间接调用
JavaScript中函数也是对象,和其余JavaScript对象没什么两样,函数对象也能够包含方法,其中两个方法call()和apply()能够用来间接地调用函数
函数的形参和实参
ECMAScript6中支持默认参数了
// 支持默认参数 function testFunc(x = 1, y = 2){ this.x = x; this.y = y; console.log(this.x, this.y) } testFunc();
将对象属性用做实参
相似python中传字典的key:value
// 定义一个函数接收指定参数 function arrayCopy(/* array */ from, /* index*/ from_start = 0 , /* array */ to, /* index*/ to_start = 0 , /* integer */ length) { /* 逻辑代码*/ } // 定义一个函数接收对象属性 function easyCopy(args){ arrayCopy(args.from, args.from_start, args.to, args.to_start, args.length, ) } let a = [1, 2, 3, 4,] , b = []; easyCopy({'from': a, 'to': b, 'length': 4}); console.log(a, b);
JavaScript真是一个畸形语言waht's the fuck
构造函数和原型对象,参考本文“对象-原型对象”
在JavaScript中类也是一个对象,它的建立是经过原型对象来建立的
// 定义一个类 开头大写 function Person(x, y) { // 实例化两个对象属性不共享保存在各自的实例中 this.x = x; this.y = y; } // 原型重写, 这里面的属性和方法是共享的 Person.prototype = { constructor : Person, // 指定原型对象,也就是对象建立者为Person personMethod1: function () { return 'hello 帅哥' }, 'key1': 'v1', 'key2': 'v2', }; let person1 = new Person(1, 2); console.log(person1.personMethod1());
在ES6之后出现了Class可是本质上class能够看作一个语法糖,只是让原型更加清晰而已
ES6 的类,彻底能够看做构造函数的另外一种写法。
class Point { // ... } console.log(typeof Point); // "function" 本质上是一个函数 console.log(Point === Point.prototype.constructor); // true 原型
构造函数的prototype
属性,在 ES6 的“类”上面继续存在。事实上,类的全部方法都定义在类的prototype
属性上面。
class Person{ constructor(){} toValue() {} toAdd() {} } // 等同于 Person.prototype = { constructor(){}, toValue() {}, toAdd() {}, };
另外,类的内部全部定义的方法,都是不可枚举的(non-enumerable)。
与 ES5 同样,实例的属性除非显式定义在其自己(即定义在this
对象上),不然都是定义在原型上(即定义在class
上)。
//定义类 class Person { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } addMethod(){ return '(' + this.x + ', ' + this.y + ')'; } } let person1 = new Person(2, 3); console.log(person1.toString()); // (2, 3) console.log(person1.hasOwnProperty('x')); // true console.log(person1.hasOwnProperty('y'));// true console.log(person1.hasOwnProperty('toString')); // false console.log(person1.__proto__.hasOwnProperty('toString')); // true console.log(person1.__proto__.hasOwnProperty('addMethod')); // true
类的静态方法和属性
类至关于实例的原型,全部在类中定义的方法,都会被实例继承。若是在一个方法前,加上static
关键字,就表示该方法不会被实例继承,而是直接经过类来调用,这就称为“静态方法”。
class Tools{ static makeWorker(){ return "Hello World" } } console.log(Tools.makeWorker()); // 静态方法能够直接经过类调用
感受愈来愈像python了,不过JavaScript在升级的过程当中不断吸收其余语言的专长也是好事 已入坑
静态属性指的是 Class 自己的属性,即Class.propName
,而不是定义在实例对象(this
)上的属性。
class Foo { } Foo.prop = 1; Foo.prop // 1
上面的写法为Foo
类定义了一个静态属性prop
。
目前,只有这种写法可行,由于 ES6 明确规定,Class 内部只有静态方法,没有静态属性。
es7 能够直接声明了不加this~~