1、Class的基本用法

概述

JavaScript语言的传统方式是通过构造函数,定义并生成新对象。这种写法和传统的面向对象语言差异很大,下面是一个例子:

function Point(x, y) {this.x = x;this.y = y;
};
Point.prototype.toString = function () {return '(' + this.x + ',' + this.y + ')';
};
var p = new Point(1, 2);
console.log(p.x); // 1

View Code

为了让程序员不再感到困惑和使JavaScript这门当初的玩具语言成为一款能够构建大型应用程序的高级货,ES6引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看做只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。现在使用ES6的类写法重新编写上面那个例子,查看并分析它们之间的差异:

//定义Point类
class Point {constructor(x, y) {this.x = x;this.y = y;}toString() {return '(' + this.x + ',' + this.y + ')';}
}
var p = new Point(1, 2);
console.log(p.y); // 调用属性
console.log(p.toString()); // 调用方法

View Code

【分析】

上面的代码定义了一个类,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5的构造函数Point,对应ES6的Point类的构造方法。

【注意】

Point类除了构造方法,还定义了一个toString方法。在定义类的方法的时候,前面不需要加上function这个关键字,另外,方法之间不需要逗号分隔,加了会报错。

【拓展】

构造函数的prototype属性,在ES6的类上面继续存在,事实上,类的所有方法都定义在类的prototype属性上面,怎么来理解这句话呢?当然得贴上代码

class Point {constructor() {}toString() {}toValue() {}
}
// 等同于
Point.prototype = {toString() {},toValue() {}
}

View Code

在类的实例上调用方法,其实就是调用原型上的方法

class B {}
let b = new B();
b.constructor === B.prototype.constructor // true

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法。

class Point {constructor() {}
}
Object.assign(Point.prototype, {toString() {},toValue() {}
})

View Code

prototype对象的constructor属性,直接指向类的本身,这与ES5的行为一致。

Point.prototype.constructor === Point //true

另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable),怎么来理解,下面请看一张对比图:

类的属性名,可以采用表达式

let methodName = "getArea";
class Square {constructor(length) {}[methodName]() {}
}

View Code

上面代码中,Square类的方法名getArea是从表达式得到的。

constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必选有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加。constructor方法默认返回实例对象(即this),完全可以指定返回一个对象。

class Foo {constructor() {return Object.create(null);}
}
new Foo() instanceof Foo //false

View Code

上面代码中,constructor函数返回一个全新的对象,结果导致实例对象不是Foo类的实例

【区分】

类的构造函数不使用new是没法调用的,会报错。然而普通构造函数的即使不用new也可以执行,不报错

class Foo {constructor() {return Object.create(null);}
}
Foo() //报错

View Code

类的实例对象

生成类的实例对象的写法,与ES5完全不一样,也是使用new命令,如果忘记加上new,像调用函数那样调用class,将会报错。另外一点,ES6与ES5一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)

//定义Point类
class Point {constructor(x, y) {this.x = x;this.y = y;}toString() {return '(' + this.x + ',' + this.y + ')';}
}
var p = new Point(1, 2);
console.log(p.toString()); //(2,3)
console.log(p.hasOwnProperty('x')); //true
console.log(p.hasOwnProperty('y')); //true
console.log(p.hasOwnProperty('toString')); //false
console.log(p.__proto__.hasOwnProperty('toString')); //true

View Code

上面代码中,x和y都是实例对象point自身的属性(因为定义在this变量上),所以hasOwnProperty方法返回true,而toString是原型对象的属性(因为定义在Point类上),所以返回hasOwnProperty方法返回false,这些都与ES5的行为保持一致。

【再聊几句】

与ES5一样,类的所有实例共享一个原型对象。

var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
p1.__proto__ == p2.__proto__  //true

上面的代码中,p1和p2都是Point的实例,它们的原型都是Point.prototype,所以__proto__属性是相等的。这也意味着,可以通过实例的__proto__属性为Class添加方法。

var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
p1.__proto__.printName = function(){return 'OPen'};
console.log(p1.printName()); //OPen
console.log(p2.printName()); //OPen
var p3 = new Point(5, 6);
console.log(p3.printName()); //OPen

View Code

【分析】

上面那段代码在p1的原型上添加了一个printName的方法,由于p1的原型就是p2的原型,因此p2也可以调用这个方法。而且,此后新建的实例p3也可以调用这个方法。

【注意】

以上分析意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变class的原始定义,影响到所有实例。

不存在变量提升

class不存在变量提升(hoist),这一点与ES5完全不同。

new Foo();  //报错
class Foo {}

上面代码中,Foo类使用在前,定义在后,这样会报错,因为ES6不会把类的声明提升到代码头部。这种规定的原因与继承有关,必须保证子类在父类之后定义。

{let Foo = class {};class Bar extends Foo {}
}

Class表达式

与函数一样,类也可以使用表达式的形式定义。

const Myclass = class Me {getClassName() {return Me.name;}
};

上面代码使用表达式定义了一个类。需要注意的是这个类的名字是Myclass而不是Me,Me只在Class的内部代码可用,指代当前类,如果类的内部没用到的话,可以省略Me。如下代码所示:

const Myclass = class Me {getClassName() {return Me.name;}
};
let inst = new Myclass();
console.log(inst.getClassName()); //Me
console.log(Me.name); //ReferenceError: Me is not defined

View Code

【拓展】

采用class表达式,可以写出立即执行的class。下面代码中,person是一个立即执行的类的实例。

let person = new class {constructor(name) {this.name = name;}sayName() {console.log(this.name);}
}('张三');
person.sayName(); //张三

View Code

私有方法

私有方法是常见需求,但ES6不提供,只能通过变通的方法模拟实现。一种做法是在命名上加以区别:

class Width {//公有方法
  foo(baz) {this._bar(baz);}//私有方法
  _bar(baz) {return this.snaf = baz;}
}

View Code

然而这种做法是不保险的,在类的外部,还是可以调用到这个方法

【另一种做法】

将私有方法移出模块,因为模块内部的所有方法都是对外可见的。

class Width {foo(baz) {bar.call(this, baz);}
}function bar(baz) {return this.snaf = baz;
}

View Code

上面代码中,foo是公有方法,内部调用了bar.call(this,baz),这使得bar实际上成为了当前模块的私有方法

【最后一种做法】

利用Symbol值的唯一性,将私有方法的名字命名为一个Symbol值。

const bar = Symbol('bar');
const snaf = Symbol('snaf');
export default class myClass {//公有方法
  foo(baz) {this[bar](baz);}//私有方法
  [bar](baz) {return this[snaf] = baz;}
};

View Code

上面代码中,bar和snaf都是Symbol值,导致第三方无法获取到它们,因此达到了私有方法和私有属性的效果。

this的指向

类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错(注:下面开始我要贴图了,敲代码太太累,学习效率也不高,重点放在对代码的分析上)

上面代码中,printName方法中的this默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境,因为找不到print方法而导致报错。

【解决之道1】

在构造方法中绑定this,这样就不会找不到print方法了

【解决之道2】

使用箭头函数

【解决之道3】

使用Proxy,获取方法的时候,自动绑定this。

 严格模式

类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可以用。考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。

 name属性

由于本质上,ES6的类只是ES5的构造函数的一层包装,所以函数的许多特性都被class继承,包括name属性。name属性总是返回紧跟在class关键字后面的类名。

class Point {}
Point.name  //Point

 2、Class的继承

基本用法

Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多

上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。在constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。

【注意】

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法,那么子类就得不到this对象。

ES6继承与ES5的区别

ES5的继承实质是先创造子类的实例对象this,然后将父类的方法添加到this上面(Parent.apply(this))。ES6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。

【进一步说明】

如果在ES6中,子类没有定义constructor方法,则这个方法会被默认添加。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。

【注意】

在子类的构造函数中只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。

生成上述子类实例的代码:

上面代码中,实例对象cp同时是ColorPoint和Point两个类的实例,这与ES5的行为完全一致。

类的prototype属性和__proto__属性

大多数浏览器的ES5实现中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性,Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承连。

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

【分析】

上面代码这样的结果是因为:类的继承是按照下面的模式来实现的。

《对象的扩展》一章给出过Object.setPrototypeOf方法的实现,因此就得到了上面的结果。

【再唠叨几句】

这两条继承链,可以这样理解:

作为一个对象,子类(B)的原型(__proto__属性)是父类(A);

作为一个构造函数,子类(B)的原型(prototype属性)是父类(A)的实例。

 Extends的继承目标

extends关键字后面可以跟多种类型的值,下面代码的A,只要是一个有prototype属性的函数,就能被B继承。由于函数都有prototype属性(除了Function.prototype函数),因此A可以是任意函数。

class B extends A {}

【拓展】

(1)三种继承目标特殊情况之子类继承Object类。这种情况下,A其实就是构造函数Object的复制,A的实例就是Object的实例。

(2)三种继承目标特殊情况之不存在任何继承。这种情况下,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承Function.prototype。但是,A调用后返回一个空对象(即Object实例),所以A.prototype.__proto__指向构造函数(Object)的prototype属性。

(3)三种继承目标特殊情况之子类继承null。这种情况与第二种情况非常像,A也是一个普通函数,所以直接继承Function.prototype。但是,A调用后返回的对象不继承任何方法,所以它的__proto__指向Function.prototype。

其实质上执行了下面的代码

Object.getPrototypeOf()

Object.getPrototypeOf方法可以用来从子类上获取父类。因此这个方法可以来判断一个类是否继承了另一个类。

super关键字

super这个关键字,既可以当做函数使用,也可以当做对象使用。在这两种情况下,它的用法完全不同。

(1)作为函数调用:代表父类的构造函数,ES6要求子类的构造函数必须执行一次super函数。

【注意】

super虽然代表父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)。并且作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。请看下面的两张图片:

     

(2)作为对象使用:在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

上面代码中,子类B当中的super.p(),就是讲super当做一个对象来使用。这时,super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p()。

【注意】

由于super指向父类的原型对象,所以定义在父类实例上的方法或者属性,是无法通过super调用的。比如下面代码,p是父类A实例的属性,super.p就引用不到它。

如果属性定义在父类的原型对象上,super就可以获取到。比如下面代码,属性x是定义在A.prototype上的,所以super.x可以取到它的值。

【拓展1】

ES6规定,通过super调用父类的方法时,super会绑定子类的this。

上面代码中,super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()会绑定子类B的this,导致输出的是2,而不是1。也就是说,实际上执行的是super.print.call(this)。

【拓展2】

由于绑定子类的this,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。如下面代码中,super.x赋值为3,这时等同于对this.x赋值为3,而当读取super.x的时候,读的是A.prototype.x,所以返回undefined。

【拓展3】

如果super作为对象,用来静态方法之中,这时super将指向父类,而不是父类的原型对象。比如在下面代码中,super在静态方法之中指向父类,在普通方法之中指向父类的原型对象。

【注意】

使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。

下面代码中,super.valueOf()表明super是一个对象,因此就不会报错。同时,由于super绑定B的this,所以super.valueOf()返回的是一个B的实例。

【再唠叨唠叨】

由于对象总是继承其他对象的,所以可以在任意一个对象中,使用super关键字。

实例的__proto__属性

子类实例的__proto_属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。

上面代码中,ColorPoint继承了Point,导致前者原型的原型是后者的原型。因此,通过子类实例的__proto__.proto__属性,可以修改父类实例的行为。

上面代码在ColorPoint的实例p2上向Point类添加方法,结果影响到了Point的实例p1。

3、原生构造函数的继承

原生构造函数是指语言内置的构造函数,通常用来生成数据结构。ECMAScript的原生构造函数大致有下面这些:

  • Boolean()
  • Number()
  • String()
  • Array()
  • Date()
  • Funtion()
  • RegExp()
  • Error()
  • Object()

以前,这些原生构造函数是无法继承的。比如,不能自己定义一个Array的子类。下面左边代码定义了一个继承Array的MyArray类,但是,这个类的行为与Array完全不一致(右边代码)

    

【分析1】

之所以会发生这种情况,是因为子类无法获得原生构造函数的内部属性,通过Array.apply()或者分配给原型对象都不行。原生构造函数会忽略apply方法传入的this,也就是说,原生构造函数的this无法绑定,导致拿不到内部属性。

【分析2】

ES5是先新建子类的实例对象this,再将父类的属性添加到子类上,由于父类的内部属性无法获取,导致无法继承原生的构造函数。比如,Array构造函数有一个内部属性 [[DefineOwnProperty]],用来定义新属性时,更新length属性,这个内部属性无法在子类中获取,导致子类的length属性行为不正常。下面,我们想让一个普通对象继承Error对象:

上面代码中,我们想通过Error.call(e)这种写法,让普通对象e具有Error对象的实例属性。但是,Error.call()完全忽略传入的第一个参数,而是返回 一个新对象,e本身没有任何变化。这证明了Error.call(e)这种写法,无法继承原生构造函数。

【ES6】

ES6允许继承原生构造函数定义子类,因为ES6是先新建父类的实例对象this,然后再用子类的构造函数修饰this,使得父类的所有行为都可以继承。下面是一个继承Array的例子

上面代码定义了一个MyArray类,继承了Array构造函数,因此就可以从MyArray生成数组的实例。这意味着,ES6可以自定义原生数据结构(比如Array、String等)的子类,这是ES5无法做到的。

【拓展1】

上面那个例子也说明,extends关键字不仅可以用来继承类,还可以用来继承原生的构造函数。因此可以在原生数据结构的基础上,定义自己的数据结构,下面就是定义了一个带版本功能的数组。

上面代码中,VersonedArray结构会通过commit方法,将自己的当前状态存入history属性,然后通过revert方法,可以撤销当前版本,回到上一个版本。除此之外,VersionedArray依然是一个数组所有原生的数组方法都可以在它上面调用。

【自定义Error子类】

【注意】

继承Object的子类,有一个行为差异。下面代码中,NewObj继承了Object,但是无法通过super方法向父类Object传参。这是因为ES6改变了Object构造函数的行为,一旦发现Object方法不是通过new Object()这种形式调用,ES6规定Object构造函数会忽略参数。

4、Class的取值函数(getter)和存值函数(setter)

与ES5一样,在Class内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

【拓展】

存值函数和取值函数是设置在属性descriptor对象上的。

上面代码中,存值函数和取值函数是定义在html属性的描述对象上面,这与ES5完全一致。

5、Class的Generator方法

如果某个方法之前加上星号(*),就表示该方法是一个Generator函数。下面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个Generator函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器。

6、Class的静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就被称为“静态方法”。下面代码中,Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

【拓展1】

父类的静态方法,可以被子类继承。下面代码中,父类Foo有一个静态方法,子类Bar可以调用这个方法。

【拓展2】

静态方法也是可以从super对象上调用的。

 7、Class的静态属性和实例属性

静态属性指的是class本身的属性,即class.propname,而不是定义在实例对象(this)上的属性。下面的写法为Foo类定义了一个静态属性prop。

【注意】

目前,只有这种写法可行,因为ES6明确规定,class内部只有静态方法,没有静态属性。

8、类的私有属性

目前,有一个提案,为class加了私有属性。方法是在属性名之前,使用#表示。下面代码中,#x就表示私有属性x,在Point类之外是读取不到这个属性的。还可以看到,私有属性与实例的属性是可以同名的(比如:#x与get x())。

9、new.target属性

new是从构造函数生成实例的命令。ES6为new命令引入了一个new.target属性,在构造函数中返回new命令作用于那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造是怎么调用的。

上面代码确保构造函数只能通过new命令调用。

【拓展】

class内部调用new.target,返回当前class。

【注意】

子类继承父类时,new.target会返回子类。

利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。

上面代码中,Shape类不能被实例化,只能用于继承。

10、Mixin模式的实现

Mixin模式指的是,将多个类的接口混入(mix in)另一个类,它在ES6的实现如下:

上面代码的mix函数,可以将多个对象合成为一个类。使用的时候,只要继承这个类即可。

11、ES5继承与ES6继承的写法大比拼

(1)ES5

//ES5父类
function User(name, age) {this.name = name;this.age = age;
}
//静态方法
User.getClassName = function () {return 'User';
};
//类的方法
User.prototype.changeName = function (name) {this.name = name;
};
User.prototype.changeAge = function (name) {this.age = age;
};
Object.defineProperty(User.prototype, 'info', {get() {return 'name:' + this.name + ' | age:' + this.age;}
});
var user = new User('feng', 23);
console.log(user.info); // name:feng | age:23//子类
function Manager(name, age, password) {User.call(this, name, age);this.password = password;
}
//继承静态方法
Manager.__proto__ = User;
//继承prototype方法
Manager.prototype = User.prototype;
//添加新方法
Manager.prototype.changePassword = function (pwd) {this.password = password;
};
var manager = new Manager('feng', 22, '556677');
console.log(manager.name); //feng
//调用changeName方法
manager.changeName('feng xiong');
console.log(manager.name); //feng xiong

View Code

(2)ES6

//ES6父类
class User {constructor(name, age) {this.name = name;this.age = age;}//静态方法
  static getClassName() {return 'User';}//类的方法
  changeName(name) {this.name = name;}changeAge(age) {this.age = age;}get info() {return 'name:' + this.name + ' | age:' + this.age;}
}
var user = new User('fengg', 233);
console.log(user.info); // name:fengg | age:233//子类
class Manager extends User {constructor(name, age, password) {super(name, age);this.password = password;}changePassword(password) {this.password = password;}get info(){var info = super.info;console.log(info);return info + ' -- new';}
}
var manager = new Manager('ES6feng', 22, '556677');
console.log(manager.name); //ES6feng
manager.changeName('jack');
console.log(manager.name); //jack

console.log(manager.info);
// name:jack | age:22
// test.js:88 name:jack | age:22 -- new

View Code

转载于:https://www.cnblogs.com/fengxiongZz/p/8176326.html

ES6入门——类的概念相关推荐

  1. ES6快速入门——类与对象

    ES6快速入门--类与对象 1. 类 1.1 定义类 1.2 构造函数 1.3 属性与方法 1.3.1 属性 1.3.1 方法 1.4 继承 1.5 重写 1.6 super 2. 对象 2.1 创建 ...

  2. 跟面向对象卯上了,看看ES6的“类”

    上回我们说到ES5的面向对象,以及被大家公认的最佳的寄生组合式继承.时代在进步,在ES6中对于面向对象这个大boss理所应当地进行了一次大改,从原先那种比较长的写法转变为"小清新" ...

  3. 听说你对 ES6 class 类还不是很了解

    大家好,我是若川.最近组织了源码共读活动,感兴趣的可以加我微信 ruochuan12 参与. 前言 在ES5中是原型函数,到了ES6中出现了"类"的概念.等同于是ES5的语法糖,大 ...

  4. JavaScript入门几个概念

    JavaScript入门几个概念 刚刚入门JavaScript的时候,搞懂DOM.BOM以及它们的对象document和window很有必要. DOM是为了操作文档出现的API,document是它的 ...

  5. ES6入门笔记(一)

    ES6入门笔记(一) 安装babel 由于浏览器对ES6的支持还不是很好,编写ES6代码前我们要安装一个babel工具将ES6代码编译成ES5代码,用如下命令安装babel: npm install ...

  6. es6入门6--数组拓展运算符,Array.from()基本用法

    本文只是作为ES6入门第九章学习笔记,在整理知识点的同时,会加入部分个人思考与解答,若想知道更详细的介绍,还请阅读阮一峰大神的ES6入门 一.拓展运算符 ES6中新增了拓展运算(...)三个点,它的作 ...

  7. Kafka教程(一)基础入门:基本概念、安装部署、运维监控、命令行使用

    Kafka教程(一)基础入门 1.基本概念 背景 领英->Apache 分布式.消息发布订阅系统 角色 存储系统 消息系统 流处理平台-Kafka Streaming 特点 高吞吐.低延迟 cg ...

  8. es6 class类与class类中constructor

    序言 在es6 中的class关键字用于声明类,在此之前js一直没有类的概念,本文只要讨论class的与es5中对象的关系以及class中constructor的作用 关键字class ES6 的cl ...

  9. es6入门到五连绝世之三杀(triple kill )

    es6入门到五连绝世之三杀(triple kill ) 欢迎来到e6语法笔记教学 一.Promise 简介 1.1.Promise 异步执行顺序 1.2.自定义 promise 1.3.自定义Prom ...

最新文章

  1. 'utf-8' codec can't decode byte 0xb6 in position 34: invalid start byte
  2. authpuppy mysql_authpuppy 认证服务器搭建
  3. OpenCV基于LeNet-5和连接组件分析的数字识别的实例(附完整代码)
  4. ajax保存避免重复提交,ajax 实现防止重复提交
  5. 取消xp开机默认登陆账户
  6. HTTP缓存策略 304
  7. javascript代码_认识 JavaScript 可执行代码
  8. CentOS 6.x通过yum安装php7.1及相关环境
  9. mysql关系代数表达式,【数据库复习】关系代数
  10. 强化学习的A3C算法应用(训练Atari游戏)
  11. 阿里云分布式架构云平台解决方案
  12. 【SMTP】服务端口
  13. 如何解决u盘不能拷贝超过4G的大文件
  14. 方面和服务,差别大吗?
  15. python3使用蓝本Blueprint
  16. XtraReport绑定数据源的三种方式
  17. opengles之展翅飞翔的雄鹰
  18. 一个项目的大体架构及想法。
  19. java implements的用法总结
  20. 准确率99.9%的离线IP地址定位库

热门文章

  1. powerdesigner12.5入门教程
  2. DevExpress控件使用小结
  3. LeetCode之Max Points on a Line Total
  4. Flutter 雷达扫描效果、Flutter旋转扫描
  5. redis缓存失效及解决方案
  6. Ajax补充之serialize
  7. adb查看安卓设备系统Android版本
  8. 00018_流程控制语句switch
  9. notejs环境搭建
  10. tcp协议之三次握手,四次挥手