导语

class只是语法糖,并没有为js引入一种新的对象继承模式,之前通过原型链一样可以实现class的功能;
  1. //定义类
  2. class Point {
  3. constructor(x, y) {
  4. this.x = x;
  5. this.y = y;
  6. }
  7. toString() {
  8. return '(' + this.x + ', ' + this.y + ')';
  9. }
  10. }

定义class

class 就像是特殊的函数,和创建函数一样,有类声明(class declarations),和类表达式( class expressions )两种创建类的方式:


类声明

使用class 关键字,后面跟上类名(class 关键字后面的是类名)
  1. class Rectangle {
  2. constructor(height, width) {
  3. this.height = height;
  4. this.width = width;
  5. }
  6. }
类声明和普通函数声明不同的地方在于,类声明并没有函数提升(下面要介绍的类表达式也没有函数提升):

  1. var obj = new Myclass(); //报错
  2. class Myclass (){ }
类声明不能函数提升是为了保证,子类继承父类的那个语句,不会提升至头部,否则将会出现父类还没有定义,子类就要继承, 看下面的例子:

  1. {
  2. let B = class {}; // let 声明 不存在函数提升
  3. class A extends B { //如果存在类哈函数提升的话,这行会提升到第一行,父亲还没声明,儿子怎么继承?
  4. }
  5. }
类声明不能和已经存在的类重名,(不管这个类之前是通过类声明的方式声明还是通过类表达式的方式声明的), 否则将会报错;
  1. class f1 {};
  2. class f1 {};
  3. var f2 = class {};
  4. class f2 {}; // 报错了
  5. class f3 {};
  6. var f3 = class {}; // 报错了
  7. var f4 = class {};
  8. var f4 = class {}; // 如果两个函数表达式重名了,那么不会报错

类表达式

类表达式是定义类的另外一种方式
  1. var myClass = class [className] [extends] {
  2. // class body
  3. }
就像函数表达式一样,在类表达式中,类名是可有可无的。若定义的类名,则该类名只有的类的内部才可以访问到。

  1. // 方式一
  2. const MyClass = class {};
  3. // 方式二:给出类名
  4. const MyClass = class Me {
  5. getClassName() {
  6. return Me.name;
  7. }
  8. };
如果class 后面没有名字,那么该类.name  就是 函数表达式的名字:
  1. var Foo = class {
  2. constructor() {}
  3. bar() {
  4. return 'Hello World!';
  5. }
  6. };
  7. var instance = new Foo();
  8. instance.bar(); // "Hello World!"
  9. Foo.name; // "Foo"
如果 class 后面有名字,那么该名字只能在函数内被访问到,同时该类 . name 就是class 后面的名字:

  1. var Foo = class NamedFoo {
  2. constructor() {}
  3. whoIsThere() {
  4. return NamedFoo.name;
  5. }
  6. }
  7. var bar = new Foo();
  8. bar.whoIsThere(); // "NamedFoo"
  9. NamedFoo.name; // ReferenceError: NamedFoo is not defined
  10. Foo.name; // "NamedFoo"
采用类表达式,可以写出立即执行的Class。如下:

  1. let person = new class {
  2. constructor(name) {
  3. this.name = name;
  4. }
  5. sayName() {
  6. console.log(this.name);
  7. }
  8. }('Zhang San');
  9. person.sayName(); // Zhang San

类体和方法定义

类的成员需要定义在一对大括号内{},大括号内的代码的大括号本身组成了类体。类成员包括类构造器类方法 (包括静态方法和实例方法)。
类体中的代码都强制在严格模式中执行,即默认”use strict”。考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。
构造器(constructor方法)

一个类只能拥有一个名为constructor的方法(否则会报错),一个类的 constructor 方法只有在实例化的时候被调用。

如果没有显式定义constructor方法,这个方法会被默认添加,即,不管有没有显示定义,任何一个类都有constructor方法。

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

  1. class Point {}
  2. class ColorPoint extends Point {
  3. constructor() {}
  4. }
  5. let cp = new ColorPoint(); // ReferenceError
上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错。

原型方法

定义类的方法时,方法名前面不需要加上function关键字。另外,方法之间不需要用逗号分隔,加了会报错。

  1. class Bar {
  2. constructor() {}
  3. doStuff() {}
  4. toString() {}
  5. toValue() {}
  6. }
上面的写法就等同于下面:
  1. Bar.prototype = {
  2. doStuff() {},
  3. toString() {},
  4. toValue() {}
  5. };
所以,在类的实例上调用方法,实际上就是调用原型上的方法。既然类的方法都是定义在prototype上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法。

  1. class Point {
  2. constructor() {
  3. // ...
  4. }
  5. }
  6. Object.assign(Point.prototype, {
  7. toString() {},
  8. toValue() {}
  9. });
另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
  1. class Point {
  2. constructor(x, y) {
  3. // ...
  4. }
  5. toString() {
  6. return '(' + x + ', ' + y + ')';
  7. }
  8. }
  9. Object.keys(Point.prototype); // []
  10. Object.getOwnPropertyNames(Point.prototype); // ["constructor", "toString"]
  11. Object.getOwnPropertyDescriptor(Point, 'toString');
  12. // Object {writable: true, enumerable: false, configurable: true}

静态方法
static关键字用来定义类的静态方法。静态方法是指那些不需要对类进行实例化,使用类名就可以直接访问的方法。静态方法经常用来作为工具函数。

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. static distance(a, b) {
  7. const dx = a.x - b.x;
  8. const dy = a.y - b.y;
  9. return Math.sqrt(dx*dx + dy*dy);
  10. }
  11. }
  12. const p1 = new Point(5, 5);
  13. const p2 = new Point(10, 10);
  14. console.log(Point.distance(p1, p2));

静态方法不可以被实例继承,是通过类名直接调用的。但是,父类的静态方法可以被子类继承。

  1. class Foo {
  2. static classMethod() {
  3. return 'hello';
  4. }
  5. }
  6. class Bar extends Foo {
  7. }
  8. Bar.classMethod(); // "hello"
extends关键字

extends关键字用于实现类之间的继承。子类继承父类,就继承了父类的所有属性和方法。 extends后面只可以跟一个父类。

  1. class ColorPoint extends Point {
  2. constructor(x, y, color) {
  3. super(x, y); // 调用父类的constructor(x, y)
  4. this.color = color;
  5. }
  6. toString() {
  7. return this.color + ' ' + super.toString(); // 调用父类的toString()
  8. }
  9. }
extends关键字不能用于继承一个对象,如果你想继承自一个普通的对象,你必须使用 Object.setPrototypeof ( )

es5 的继承和 es6 的继承
es5中的原型链继承,就是通过将子类构造函数的原型作为父类构造函数的实例(sub.prototype=new super),这样就连通了子类-子类原型-父类;
  1. //先来个父类,带些属性
  2. function Super(){
  3. this.flag = true;
  4. }
  5. //为了提高复用性,方法绑定在父类原型属性上
  6. Super.prototype.getFlag = function(){
  7. return this.flag;
  8. }
  9. //来个子类
  10. function Sub(){
  11. this.subFlag = false;
  12. }
  13. //实现继承
  14. Sub.prototype = new Super;
  15. //给子类添加子类特有的方法,注意顺序要在继承之后
  16. Sub.prototype.getSubFlag = function(){
  17. return this.subFlag;
  18. }
  19. //构造实例
  20. var es5 = new Sub;
但是这样的原型链继承有问题:我们的目标是构造函数的属性私有化,方法复用化,所以我们把属性放在函数内,把方法放到原型上;但是原型链继承显然,父类的属性和方法都放到了子类的原型上;
为了解决上面的做法,我们在es5中混合使用 构造函数call 继承;
  1. function Super(){
  2. this.flag = true;
  3. }
  4. Super.prototype.getFlag = function(){
  5. return this.flag; //继承方法
  6. }
  7. function Sub(){
  8. this.subFlag = flase
  9. Super.call(this) //继承属性
  10. }
  11. Sub.prototype = new Super;
  12. var obj = new Sub();
  13. Sub.prototype.constructor = Sub;
  14. Super.prototype.getSubFlag = function(){
  15. return this.flag;
  16. }
但是还有个小问题是,子类.prototype = new 父类,子类.prototype的constructor 就指向了父类,所以我们要重写一下:
  1. Sub.prototype.constructor = Sub;
ES6的继承实现方法,其内部其实也是ES5组合继承的方式,通过call构造函数,在子类中继承父类的属性,通过原型链来继承父类的方法。

我们将 extend 用babel 进行转码:
  1. function _inherits(subClass, superClass) {
  2. // 确保superClass为function
  3. if (typeof superClass !== "function" && superClass !== null) {
  4. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  5. }
  6. // 把子类.prototype 继承了父类.prototype(new 父类), 同时把子类prototype的constructor进行了重写;
  7. // 给subClass添加constructor这个属性
  8. subClass.prototype = Object.create(superClass && superClass.prototype, {
  9. constructor: {
  10. value: subClass,
  11. enumerable: false,
  12. writable: true,
  13. configurable: true
  14. }
  15. });
  16. // 将父类设为子类的prototype
  17. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  18. }
里面 子类. prototype = Object.create ( 父类.prototype )这句话,实际上和下面的代码类似:
  1. 子类.prototype = new 父类
不同的是, 子类. prototype = Object.create ( 父类.prototype )不会继承父类的constructor里面的属性,只会继承父类prototype上的方法;

一个关于 Object.create(car.prototype) 用法的代码:
  1. function Car (desc) {
  2. this.desc = desc;
  3. this.color = "red";
  4. }
  5. Car.prototype = {
  6. getInfo: function() {
  7. return 'A ' + this.color + ' ' + this.desc + '.';
  8. }
  9. };
  10. //instantiate object using the constructor function
  11. var car = Object.create(Car.prototype);
  12. car.color = "blue";
  13. alert(car.getInfo()); //displays 'A blue undefined.' ??! // 看见了吧,只会继承方法,不能继承属性
当你只想继承类的原型,而不想继承类的constructor的时候,使用Object.create 是很棒的选择;

如果我们想子类继承父类的prototype ,同时子类也要有自己的属性,请看下面的代码:
  1. var Car2 = Object.create(null); //this is an empty object, like {}
  2. Car2.prototype = {
  3. getInfo: function() {
  4. return 'A ' + this.color + ' ' + this.desc + '.';
  5. }
  6. };
  7. var car2 = Object.create(Car2.prototype, {
  8. //value properties
  9. color: { writable: true, configurable:true, value: 'red' },
  10. //concrete desc value
  11. rawDesc: { writable: false, configurable:true, value: 'Porsche boxter' },
  12. // data properties (assigned using getters and setters)
  13. desc: {
  14. configurable:true,
  15. get: function () { return this.rawDesc.toUpperCase(); },
  16. set: function (value) { this.rawDesc = value.toLowerCase(); }
  17. }
  18. });
  19. car2.color = 'blue';
  20. alert(car2.getInfo()); //displays 'A RED PORSCHE BOXTER.'
每一个属性又是一堆属性的集合,又称descriptor, 分为 data descriptor 和 accessor(访问 ) descriptor

更多的知识:http://www.htmlgoodies.com/beyond/javascript/object.create-the-new-way-to-create-objects-in-javascript.html
总之,extends做了两件事情,一个是通过Object.create()把子类的原型赋值为父类的实例, 实现了继承方法,子类.prototype.__proto__也自动指向父类的原型,一个是手动修改了子类的__proto__, 修改为指向父类,(本来在es5 中应该是指向Function.prototype);
在子类中必须执行的super()方法,实际上是用call 方法:
  1. var _this = _possibleConstructorReturn(this, (b.__proto__ || Object.getPrototypeOf(b)).call(this));
父类被当成普通函数来执行,从而将this绑定到子类上;

同时,extend后面可以跟多种类型的值:
第一种特殊情况,子类继承Object类。
  1. class A extends Object {
  2. }
  3. A.__proto__ === Object // true
  4. A.prototype.__proto__ === Object.prototype // true
第二种特殊情况,不存在任何继承。

  1. class A {
  2. }
  3. A.__proto__ === Function.prototype // true
  4. A.prototype.__proto__ === Object.prototype // true
第三种特殊情况,子类继承null。

  1. class C extends null {
  2. constructor() { return Object.create(null); }
  3. }
两条继承链

一个继承语句同时存在两条继承链:一条实现属性继承,一条实现方法继承.
  1. class A extends B {}
  2. A.__proto__ === B; //继承属性
  3. A.prototype.__proto__ === B.prototype; //继承方法
ES6的子类的__proto__是父类,子类的原型的__proto__是父类的原型
第二条继承链理解起来没有什么问题,es6 本身就是对es5 混合模式继承的封装,在原型继承上,es6使用的是 
  1. 子类.prototype = Object.create (父类.prototype) // 相当于 new 父类
子类的原型是父类的实例(暂时这样理解,其实子类并不能继承父类的属性,只能继承方法),所以子类.prototype(父类的实例)指向父类的prototype。

但是第一个继承链就不好理解了,在ES5中 子类.__proto__是指向Function.prototype的,因为每一个构造函数其实都是Function这个对象构造的。在ES6的继承中,有这样一句话:
  1. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
es6 的 extends 把子类的__proto__指向父类可以实现属性的继承,在ES5中在没有用借用继承的时候由于父类属性被子类原型继承,所有的子类实例实际上都是同一个属性引用。

可以这么说,在ES5继承和构造实例,ES6构造实例的时候可以理解__proto__指向构造函数的原型的,但是在ES6继承中,__proto__指继承自哪个类或原型。也就是说,两条继承链只存在于两个类之间的关系,实例与构造函数之间的关系,还是es5的模式;
  1. var p1 = new Point(2,3);
  2. var p2 = new Point(3,2);
  3. p1.__proto__ === p2.__proto__
这也意味着,可以通过实例的__proto__属性为Class添加方法。
  1. var p1 = new Point(2,3);
  2. var p2 = new Point(3,2);
  3. p1.__proto__.printName = function () { return 'Oops' };
  4. p1.printName() // "Oops"
  5. p2.printName() // "Oops"
  6. var p3 = new Point(4,2);
  7. p3.printName() // "Oops"
但是我们不推荐这样做

super 关键字

super关键字可以用来调用其父类的构造器或方法。super 作为方法的时候,必须在 constructor 中调用,并且只能在 constructor 里面被调用

  1. class Cat {
  2. constructor(name) {
  3. this.name = name;
  4. }
  5. speak() {
  6. console.log(this.name + ' makes a noise.');
  7. }
  8. }
  9. class Lion extends Cat {
  10. speak() {
  11. super.speak();
  12. console.log(this.name + ' roars.');
  13. }
  14. }
super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)。
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象,可以调用原型上的方法(但是父类的私有属性和方法就调用不到了);在静态方法中,指向父类。
  1. class A {
  2. p() {
  3. return 2;
  4. }
  5. }
  6. class B extends A {
  7. constructor() {
  8. super();
  9. console.log(super.p()); // 2
  10. }
  11. }
  12. let b = new B();
ES6 规定,通过super调用父类的方法时,super会绑定子类的this。

  1. class A {
  2. constructor() {
  3. this.x = 1;
  4. }
  5. print() {
  6. console.log(this.x);
  7. }
  8. }
  9. class B extends A {
  10. constructor() {
  11. super();
  12. this.x = 2;
  13. }
  14. m() {
  15. super.print();
  16. }
  17. }
  18. let b = new B();
  19. b.m() // 2
通过super对某个属性赋值,super 的this 指向子类,如果要访问,super 的 this 就变成了父类的prototype:
  1. class A {
  2. constructor() {
  3. this.x = 1;
  4. }
  5. }
  6. class B extends A {
  7. constructor() {
  8. super();
  9. this.x = 2;
  10. super.x = 3;
  11. console.log(super.x); // undefined
  12. console.log(this.x); // 3
  13. }
  14. }
  15. let b = new B();
如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。
  1. class Parent {
  2. static myMethod(msg) {
  3. console.log('static', msg);
  4. }
  5. myMethod(msg) {
  6. console.log('instance', msg);
  7. }
  8. }
  9. class Child extends Parent {
  10. static myMethod(msg) {
  11. super.myMethod(msg);
  12. }
  13. myMethod(msg) {
  14. super.myMethod(msg);
  15. }
  16. }
  17. Child.myMethod(1); // static 1
  18. var child = new Child();
  19. child.myMethod(2); // instance 2
注意,使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。

  1. class A {}
  2. class B extends A {
  3. constructor() {
  4. super();
  5. console.log(super); // 报错
  6. }
  7. }

ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。

类的Getter和Setter方法

与ES5一样,在类内部可以使用getset关键字,对某个属性设置取值和赋值方法。

  1. class Foo {
  2. constructor() {}
  3. get prop() {
  4. return 'getter';
  5. }
  6. set prop(val) {
  7. console.log('setter: ' + val);
  8. }
  9. }
  10. let foo = new Foo();
  11. foo.prop = 1;
  12. // setter: 1
  13. foo.prop;
  14. // "getter"
上面代码中,prop属性有对应 的赋值和取值方法,因此赋值和读取行为都被自定义了。 
存值和取值方法是设置在属性的descriptor对象上的。

  1. var descriptor = Object.getOwnPropertyDescriptor(Foo.prototype, 'prop');
  2. "get" in descriptor // true
  3. "set" in descriptor // true
上面代码中,存值和取值方法是定义在prop属性的描述对象上的,这与ES5一致。

类的Generator方法

如果类的某个方法名前加上星号(*),就表示这个方法是一个Generator函数。

  1. class Foo {
  2. constructor(...args) {
  3. this.args = args;
  4. }
  5. * [Symbol.iterator]() {
  6. for (let arg of this.args) {
  7. yield arg;
  8. }
  9. }
  10. }
  11. for (let x of new Foo('hello', 'world')) {
  12. console.log(x);
  13. }
  14. // hello
  15. // world
上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个Generator函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器。
new.target属性
ES6为new命令引入了一个new.target属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的(比如说calll),new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
  1. function Person(name) {
  2. if (new.target !== undefined) {
  3. this.name = name;
  4. } else {
  5. throw new Error('必须使用new生成实例');
  6. }
  7. }
  8. // 另一种写法
  9. function Person(name) {
  10. if (new.target === Person) {
  11. this.name = name;
  12. } else {
  13. throw new Error('必须使用new生成实例');
  14. }
  15. }
  16. var person = new Person('张三'); // 正确
  17. var notAPerson = Person.call(person, '张三'); // 报错
Class内部调用new.target,在new 一个实例的时候 ,返回当前Class。然而当子类继承父类时,new.target会返回子类。

  1. class Rectangle {
  2. constructor(length, width) {
  3. console.log(new.target === Rectangle);
  4. // ...
  5. }
  6. }
  7. class Square extends Rectangle {
  8. constructor(length) {
  9. super(length, length); // 相当于执行父类中的constructor,
  10. }
  11. }
  12. var obj = new Square(3); // 输出 false
利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。
  1. class Shape {
  2. constructor() {
  3. if (new.target === Shape) {
  4. throw new Error('本类不能实例化'); // 抛出一个错误
  5. }
  6. }
  7. }
  8. class Rectangle extends Shape {
  9. constructor(length, width) {
  10. super();
  11. // ...
  12. }
  13. }
  14. var x = new Shape(); // 报错
  15. var y = new Rectangle(3, 4); // 正确

null

转载于:https://www.cnblogs.com/dujuncheng/p/6907980.html

学习es6中class——整合阮一峰教程、MDN相关推荐

  1. ES6专题——整理自阮一峰老师的ECMAScript 6入门

    这里我仅仅是记录了那些我认为值得注意的ES6知识点,详细版请挪步https://es6.ruanyifeng.com/#docs/let let和const命令 let声明的变量只在它所在的代码块有效 ...

  2. 《es6标准入门》 阮一峰

    2 let和const命令 2.1 let命令 2.1.1 基本用法 2.1.2 不存在变量提升 2.1.3 暂时性死区 2.1.4 不允许重复声明 2.2 块级作用域 2.2.1 为什么需要块级作用 ...

  3. 学习react之React 入门(阮一峰教程笔记)

    一 react.js react-dom.js Brower.js react.js 核心库 react-dom.js提供dom方法 Brower.js JSX语法--->JS语法 服务器 $ ...

  4. 阮一峰php博客,在PHP语言中使用JSON – 阮一峰的网络日志

    目前,JSON已经成为最流行的数据交换格式之一,各大 我写过一篇<数据类型和JSON格式>,探讨它的设计思想.今天,我想总结一下PHP语言对它的支持,这是开发 从5.2版本开始,PHP原生 ...

  5. 程序员的自我修养--读阮一峰《如何变得有思想》有感

    前言 年前无意浏览到阮一峰的网络博客(链接),一发不可收拾,从那以后一直追着拜读其博客,并看到了其在网站上的广告(暂且称为广告)<如何变得有思想>,果断买来拜读,但是一直忙于各种事情,最近 ...

  6. 通过这个免费的,由23部分组成的互动课程,学习ES6 +

    JavaScript is undoubtedly one of the most popular programming languages in the world. It's used almo ...

  7. ES6 标准入门(第二版)阮一峰学习

    现在前端环境中,每一位程序员都要求熟练ES6语法,但是大部分ES6的文档都不太完整,接下来的时间,我将为童鞋们分享阮一峰老师第二版的ES6标准.让我们一起来学习一下!!! 本期先说一下学习的目录 1: ...

  8. 前端知乎:关于阮一峰博客《学习Javascript闭包》章节中最后两个思考题

    阮一峰博客:<学习Javascript闭包>章节中最后有个思考题: 如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了. 代码片段一 var name = "The ...

  9. 【ES6】阮一峰ES6学习(六) Proxy

    Proxy 1. 前言 2. 使用 3. Proxy 实例方法 1. get()方法 2. set()方法 3. apply()方法 4. 为什么要存在Proxy? 两者对比 1. 前言 es6中全新 ...

最新文章

  1. 2.easyui 控件取值方式
  2. 问题解决——使用CriticalSection后 0xXXXXXXXX处最可能的异常: 0xC0000005: 写入位置 0x00000014 时发生访问冲突
  3. spring bean中scope=prototype“的作用
  4. 关于extjs中动态添加TabPanel的tab项并以iframe显示的整理(转)
  5. 必背42个单词_高中英语必背100个常考单词,考试必考
  6. 实现公告板和本周热卖功能
  7. Bash脚本15分钟进阶教程-转
  8. adobe dreamweaver cs6 css,Adobe Dreamweaver CS6
  9. 特征值分解:特征值,特征向量,特征向量矩阵
  10. java 本地连接状态_本地连接受限制
  11. 查询和01号的同学学习的课程完全相同的其他同学的信息的优化
  12. 2022中国新时代100大建筑公布,重庆来福士、北京大兴机场、港珠澳大桥等杰出工程入选 | 美通社头条...
  13. 平面设计中负空间的意思是什么?如何设计?
  14. 从零到一写一个完整的 Compose 版本的天气
  15. mini2440----keil for AMR之IIC读写EEPROM(AT24C08)
  16. Android群英传笔记——第十章:Android性能优化
  17. TFT供电电路(VCOM/VGL/VGH/AVDD)设计原理
  18. 【计算机科学基础】翻译、编译、解释、汇编
  19. web前端——网页设计
  20. 智慧园区信息化解决方案,未来园区发展新方向

热门文章

  1. Spring Boot缓存实战 默认Cache(ConcurrentMapCacheManager)
  2. luoguP3799 妖梦拼木棒
  3. 计算机配件模拟,电脑装机模拟各配件跑分及计算公式分享
  4. oracle生僻字解决方案
  5. htc 8x android,htc 8x的usb驱动下载
  6. 必读的AI和深度学习博客
  7. 计算机识别不了佳能打印机,佳能IP4200打印机“无法识别墨水盒”解决办法
  8. 数加加众包:奔驰“哭诉维权”美女硕士,你“不要脸”的样子真的很美
  9. ORA-12899: value too large for column 问题解决
  10. USACO--Milking Cows (C语言)挤奶牛