TypeScript高级用法详解菜鸟教程_TypeScript小白指南

引言作为一门强大的静态类型检查工具,如今在许多中大型应用程序以及流行的JS库中均能看到TypeScript的身影。JS作为一门弱类型语言,在我们写代码的过程中稍不留神便会修改掉变量的类型,从而导致一些出乎意料的运行时错误。然而TypeScript在编译过程中便能帮我们解决这个难题,不仅在JS中引入了强类型检查,并且编译后的JS代码能够运行在任何浏览器环境,N

TypeScript高级用法详解菜鸟教程

引言

作为一门强大的静态类型检查工具,如今在许多中大型应用程序以及流行的JS库中均能看到TypeScript的身影。JS作为一门弱类型语言,在我们写代码的过程中稍不留神便会修改掉变量的类型,从而导致一些出乎意料的运行时错误。然而TypeScript在编译过程中便能帮我们解决这个难题,不仅在JS中引入了强类型检查,并且编译后的JS代码能够运行在任何浏览器环境,Node环境和任何支持ECMAScript 3(或更高版本)的JS引擎中。最近公司刚好准备使用TypeScript来对现有系统进行重构,以前使用TypeScript的机会也不多,特别是一些有用的高级用法,所以借着这次机会,重新巩固夯实一下这方面的知识点,如果有错误的地方,还请指出。

TypeScript高级用法详解菜鸟教程_TypeScript小白指南

1、类继承

在ES5中,我们一般通过函数或者基于原型的继承来封装一些组件公共的部分方便复用,然而在TypeScript中,我们可以像类似Java语言中以面向对象的方式使用类继承来创建可复用的组件。我们可以通过class关键字来创建类,并基于它使用new操作符来实例化一个对象。为了将多个类的公共部分进行抽象,我们可以创建一个父类并让子类通过extends关键字来继承父类,从而减少一些冗余代码的编写增加代码的可复用性和可维护性。示例如下:

class Parent {
    readonly x: number;
    constructor() {
        this.x = 1;
    }
    
    print() {
        console.log(this.x);
    }
}

class Child extends Parent {
    readonly y: number;
    constructor() {
        // 注意此处必须优先调用super()方法
        super();
        this.y = 2;
    }
    
    print() {
        // 通过super调用父类原型上的方法,但是方法中的this指向的是子类的实例
        super.print();
        console.log(this.y);
    }
}

const child = new Child();
console.log(child.print()) // -> 1 2

在上述示例中,Child子类中对父类的print方法进行重写,同时在内部使用super.print()来调用父类的公共逻辑,从而实现逻辑复用。class关键字作为构造函数的语法糖,在经过TypeScript编译后,最终会被转换为兼容性好的浏览器可识别的ES5代码。class在面向对象的编程范式中非常常见,因此为了弄清楚其背后的实现机制,我们不妨多花点时间来看下经过编译转换之后的代码是什么样子的(当然这部分已经比较熟悉的同学可以直接跳过)。

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    }
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Parent = /** @class */ (function () {
    function Parent() {
        this.x = 1;
    }
    Parent.prototype.print = function () {
        console.log(this.x);
    };
    return Parent;
}());
var Child = /** @class */ (function (_super) {
    __extends(Child, _super);
    function Child() {
        var _this = 
        // 注意此处必须优先调用super()方法
        _super.call(this) || this;
        _this.y = 2;
        return _this;
    }
    Child.prototype.print = function () {
        // 通过super调用父类原型上的方法,但是方法中的this指向的是子类的实例
        _super.prototype.print.call(this);
        console.log(this.y);
    };
    return Child;
}(Parent));
var child = new Child();
console.log(child.print()); // -> 1 2

以上就是转换后的完整代码,为了方便对比,这里将原来的注释信息保留,仔细研究这段代码我们会发现以下几个要点: 
1) 子类Child的构造函数中super()方法被转换成了var _this = _super.call(this) || this,这里的_super指的就是父类Parent,因此这句代码的含义就是调用父类构造函数并将this绑定到子类的实例上,这样的话子类实例便可拥有父类的x属性。因此为了实现属性继承,我们必须在子类构造函数中调用super()方法,如果不调用会编译不通过。

2) 子类Child的print方法中super.print()方法被转换成了_super.prototype.print.call(this),这句代码的含义就是调用父类原型上的print方法并将方法中的this指向子类实例,由于在上一步操作中我们已经继承到父类的x属性,因此这里我们将直接打印出子类实例的x属性的值。

3) extends关键字最终被转换为__extends(Child, _super)方法,其中_super指的是父类Parent,为了方便查看,这里将_extends方法单独提出来进行研究。

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    }
    return function (d, b) {
        // 第一部分
        extendStatics(d, b);
        
        // 第二部分
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();

在以上代码中,主要可以分为两个部分来进行理解,第一部分为extendStatics(d, b)方法,第二部分为该方法后面的两行代码。

第一部分:

在extendStatics方法内部虽然代码量相对较多,但是不难发现其实还是主要为了兼容ES5版本的执行环境。在ES6中新增了Object.setPrototypeOf方法用于手动设置对象的原型,但是在ES5的环境中我们一般通过一个非标准的__proto__属性来进行设置,Object.setPrototypeOf方法的原理其实也是通过该属性来设置对象的原型,其实现方式如下:

Object.setPrototypeOf = function(obj, proto) {
    obj.__proto__ = proto;
    return obj;
}

在extendStatics(d, b)方法中,d指子类Child,b指父类Parent,因此该方法的作用可以解释为:

// 将子类Child的__proto__属性指向父类Parent
Child.__proto__ = Parent;

可以将这行代码理解为构造函数的继承,或者叫静态属性和静态方法的继承,即属性和方法不是挂载到构造函数的prototype原型上的,而是直接挂载到构造函数本身,因为在JS中函数本身也可以作为一个对象,并可以为其赋予任何其他的属性,示例如下:

function Foo() {
  this.x = 1;
  this.y = 2;
}

Foo.bar = function() {
  console.log(3);
}

Foo.baz = 4;
console.log(Foo.bar()) // -> 3
console.log(Foo.baz) // -> 4

因此当我们在子类Child中以Child.someProperty访问属性时,如果子类中不存在就会通过Child.__proto__寻找父类的同名属性,通过这种方式来实现静态属性和静态方法的路径查找。

第二部分:

在第二部分中仅包含以下两行代码:

function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());

其中d指子类Child,b指父类Parent,这里对于JS中实现继承的几种方式比较熟悉的同学可以一眼看出,这里使用了寄生组合式继承的方式,通过借用一个中间函数__()来避免当修改子类的prototype上的方法时对父类的prototype所造成的影响。我们知道,在JS中通过构造函数实例化一个对象之后,该对象会拥有一个__proto__属性并指向其构造函数的prototype属性,示例如下:

function Foo() {
  this.x = 1;
  this.y = 2;
}

const foo = new Foo();
foo.__proto__ === Foo.prototype; // -> true

对于本例中,如果通过子类Child来实例化一个对象之后,会产生如下关联:

const child = new Child();
child.__proto__ === (Child.prototype = new __());
child.__proto__.__proto__ === __.prototype === Parent.prototype; 

// 上述代码等价于下面这种方式
Child.prototype.__proto__ === Parent.prototype;

因此当我们在子类Child的实例child对象中通过child.someMethod()调用某个方法时,如果在实例中不存在该方法,则会沿着__proto__继续往上查找,最终会经过父类Parent的prototype原型,即通过这种方式来实现方法的继承。

基于对以上两个部分的分析,我们可以总结出以下两点:

// 表示构造函数的继承,或者叫做静态属性和静态方法的继承,总是指向父类
1. Child.__proto__ === Parent;

// 表示方法的继承,总是指向父类的prototype属性
2. Child.prototype.__proto__ === Parent.prototype;

2、访问修饰符

TypeScript为我们提供了访问修饰符(Access Modifiers)来限制在class外部对内部属性的访问,访问修饰符主要包含以下三种:

  • public:公共修饰符,其修饰的属性和方法都是公有的,可以在任何地方被访问到,默认情况下所有属性和方法都是public的。
  • private:私有修饰符,其修饰的属性和方法在class外部不可见。
  • protected:受保护修饰符,和private比较相似,但是其修饰的属性和方法在子类内部是被允许访问的。

我们通过一些示例来对几种修饰符进行对比:

class Human {
    public name: string;
    public age: number;
    public constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

const man = new Human('tom', 20);
console.log(man.name, man.age); // -> tom 20
man.age = 21;
console.log(man.age); // -> 21

在上述示例中,由于我们将访问修饰符设置为public,因此我们通过实例man来访问name和age属性是被允许的,同时对age属性重新赋值也是允许的。但是在某些情况下,我们希望某些属性是对外不可见的,同时不允许被修改,那么我们就可以使用private修饰符:

class Human {
    public name: string;
    private age: number; // 此处修改为使用private修饰符
    public constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

const man = new Human('tom', 20);
console.log(man.name); // -> tom
console.log(man.age);
// -> Property 'age' is private and only accessible within class 'Human'.

我们将age属性的修饰符修改为private后,在外部通过man.age对其进行访问,TypeScript在编译阶段就会发现其是一个私有属性并最终将会报错。

编译后的代码如下:

var Human = /** @class */ (function () {
    function Human(name, age) {
        this.name = name;
        this.age = age;
    }
    return Human;
}());
var man = new Human('tom', 20);
console.log(man.name); // -> tom
console.log(man.age); // -> 20

使用private修饰符修饰的属性或者方法在子类中也是不允许访问的,示例如下:

class Human {
    public name: string;
    private age: number;
    public constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

class Woman extends Human {
    private gender: number = 0;
    public constructor(name: string, age: number) {
        super(name, age);
        console.log(this.age);
    }
}

const woman = new Woman('Alice', 18);
// -> Property 'age' is private and only accessible within class 'Human'.

在上述示例中由于在父类Human中age属性被设置为private,因此在子类Woman中无法访问到age属性,为了让在子类中允许访问age属性,我们可以使用protected修饰符来对其进行修饰:

class Human {
    public name: string;
    protected age: number; // 此处修改为使用protected修饰符
    public constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

class Woman extends Human {
    private gender: number = 0;
    public constructor(name: string, age: number) {
        super(name, age);
        console.log(this.age);
    }
}

const woman = new Woman('Alice', 18); // -> 18

当我们将private修饰符用于构造函数时,则表示该类不允许被继承或实例化,示例如下:

class Human {
    public name: string;
    public age: number;
    
    // 此处修改为使用private修饰符
    private constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

class Woman extends Human {
    private gender: number = 0;
    public constructor(name: string, age: number) {
        super(name, age);
    }
}

const man = new Human('Alice', 18);
// -> Cannot extend a class 'Human'. Class constructor is marked as private.
// -> Constructor of class 'Human' is private and only accessible within the class declaration.

当我们将protected修饰符用于构造函数时,则表示该类只允许被继承,示例如下:

class Human {
    public name: string;
    public age: number;
    
    // 此处修改为使用protected修饰符
    protected constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

class Woman extends Human {
    private gender: number = 0;
    public constructor(name: string, age: number) {
        super(name, age);
    }
}

const man = new Human('Alice', 18);
// -> Constructor of class 'Human' is protected and only accessible within the class declaration.

另外我们还可以直接将修饰符放到构造函数的参数中,示例如下:

class Human {
    // public name: string;
    // private age: number;
    
    public constructor(public name: string, private age: number) {
        this.name = name;
        this.age = age;
    }
}

const man = new Human('tom', 20);
console.log(man.name); // -> tom
console.log(man.age);
// -> Property 'age' is private and only accessible within class 'Human'.

3、接口与构造器签名

当我们的项目中拥有很多不同的类时并且这些类之间可能存在某方面的共同点,为了描述这种共同点,我们可以将其提取到一个接口(interface)中用于集中维护,并使用implements关键字来实现这个接口,示例如下:

interface IHuman {
    name: string;
    age: number;
    walk(): void;
}

class Human implements IHuman {
    
    public constructor(public name: string, public age: number) {
        this.name = name;
        this.age = age;
    }

    walk(): void {
        console.log('I am walking...');
    }
}

上述代码在编译阶段能顺利通过,但是我们注意到在Human类中包含constructor构造函数,如果我们想在接口中为该构造函数定义一个签名并让Human类来实现这个接口,看会发生什么:

interface HumanConstructor {
  new (name: string, age: number);    
}

class Human implements HumanConstructor {
    
    public constructor(public name: string, public age: number) {
        this.name = name;
        this.age = age;
    }

    walk(): void {
        console.log('I am walking...');
    }
}
// -> Class 'Human' incorrectly implements interface 'HumanConstructor'.
// -> Type 'Human' provides no match for the signature 'new (name: string, age: number): any'.

然而TypeScript会编译出错,告诉我们错误地实现了HumanConstructor接口,这是因为当一个类实现一个接口时,只会对实例部分进行编译检查,类的静态部分是不会被编译器检查的。因此这里我们尝试换种方式,直接操作类的静态部分,示例如下:

interface HumanConstructor {
  new (name: string, age: number);    
}

interface IHuman {
    name: string;
    age: number;
    walk(): void;
}

class Human implements IHuman {
    
    public constructor(public name: string, public age: number) {
        this.name = name;
        this.age = age;
    }

    walk(): void {
        console.log('I am walking...');
    }
}

// 定义一个工厂方法
function createHuman(constructor: HumanConstructor, name: string, age: number): IHuman {
    return new constructor(name, age);
}

const man = createHuman(Human, 'tom', 18);
console.log(man.name, man.age); // -> tom 18

在上述示例中通过额外创建一个工厂方法createHuman并将构造函数作为第一个参数传入,此时当我们调用createHuman(Human, ‘tom’, 18)时编译器便会检查第一个参数是否符合HumanConstructor接口的构造器签名。

4、声明合并

在声明合并中最常见的合并类型就是接口了,因此这里先从接口开始介绍几种比较常见的合并方式。

4.1 接口合并

示例代码如下:

interface A {
    name: string;
}

interface A {
    age: number;
}

// 等价于
interface A {
    name: string;
    age: number;
}

const a: A = {name: 'tom', age: 18};

接口合并的方式比较容易理解,即声明多个同名的接口,每个接口中包含不同的属性声明,最终这些来自多个接口的属性声明会被合并到同一个接口中。

4.2 函数合并

函数的合并可以简单理解为函数的重载,即通过同时定义多个不同类型参数或不同类型返回值的同名函数来实现,示例代码如下:

// 函数定义
function foo(x: number): number;
function foo(x: string): string;

// 函数具体实现
function foo(x: number | string): number | string {
    if (typeof x === 'number') {
        return (x).toFixed(2);
    }
    
    return x.substring(0, x.length - 1);
}

在上述示例中,我们对foo函数进行多次定义,每次定义的函数参数类型不同,返回值类型不同,最后一次为函数的具体实现,在实现中只有在兼容到前面的所有定义时,编译器才不会报错。

4.3 类型别名联合

类型别名联合与接口合并有所区别,类型别名不会新建一个类型,只是创建一个新的别名来对多个类型进行引用,同时不能像接口一样被实现(implements)和继承(extends),示例如下:

type HumanProperty = {
    name: string;
    age: number;
    gender: number;
};

type HumanBehavior = {
    eat(): void;
    walk(): void;
}

type Human = HumanProperty & HumanBehavior;

let woman: Human = {
    name: 'tom',
    age: 18,
    gender: 0,
    eat() {
        console.log('I can eat.');
    },
    walk() {
        console.log('I can walk.');
    }
}

class HumanComponent extends Human {
    constructor(public name: string, public age: number, public gender: number) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    
    eat() {
        console.log('I can eat.');
    }
    
    walk() {
        console.log('I can walk.');
    }
}
// -> 'Human' only refers to a type, but is being used as a value here.

5、keyof 索引查询

在TypeScript中的keyof有点类似于JS中的Object.keys()方法,但是区别在于前者遍历的是类型中的字符串索引,后者遍历的是对象中的键名,示例如下:

interface Rectangle {
    x: number;
    y: number;
    width: number;
    height: number;
}

type keys = keyof Rectangle;
// 等价于
type keys = "x" | "y" | "width" | "height";

// 这里使用了泛型,强制要求第二个参数的参数名必须包含在第一个参数的所有字符串索引中
function getRectProperty<T extends object, K extends keyof T>(rect: T, property: K): T[K] {
    return rect[property];
} 

let rect: Rectangle = {
    x: 50,
    y: 50,
    width: 100,
    height: 200
};

console.log(getRectProperty(rect, 'width')); // -> 100
console.log(getRectProperty(rect, 'notExist'));
// -> Argument of type '"notExist"' is not assignable to parameter of type '"width" | "x" | "y" | "height"'.

在上述示例中我们通过使用keyof来限制函数的参数名property必须被包含在类型Rectangle的所有字符串索引中,如果没有被包含则编译器会报错,可以用来在编译时检测对象的属性名是否书写有误。

6、Partial 可选属性

在某些情况下,我们希望类型中的所有属性都不是必需的,只有在某些条件下才存在,我们就可以使用Partial来将已声明的类型中的所有属性标识为可选的,示例如下:

// 该类型已内置在TypeScript中
type Partial<T> = {
    [P in keyof T]?: T[P]
};

interface Rectangle {
    x: number;
    y: number;
    width: number;
    height: number;
}

type PartialRectangle = Partial<Rectangle>;
// 等价于
type PartialRectangle = {
    x?: number;
    y?: number;
    width?: number;
    height?: number;
}

let rect: PartialRectangle = {
    width: 100,
    height: 200
};

在上述示例中由于我们使用Partial将所有属性标识为可选的,因此最终rect对象中虽然只包含width和height属性,但是编译器依旧没有报错,当我们不能明确地确定对象中包含哪些属性时,我们就可以通过Partial来声明。

7、Pick 部分选择

在某些应用场景下,我们可能需要从一个已声明的类型中抽取出一个子类型,在子类型中包含父类型中的部分或全部属性,这时我们可以使用Pick来实现,示例代码如下:

// 该类型已内置在TypeScript中
type Pick<T, K extends keyof T> = {
    [P in K]: T[P]
};

interface User {
    id: number;
    name: string;
    age: number;
    gender: number;
    email: string;
}

type PickUser = Pick<User, "id" | "name" | "gender">;
// 等价于
type PickUser = {
    id: number;
    name: string;
    gender: number;
};

let user: PickUser = {
    id: 1,
    name: 'tom',
    gender: 1
};

在上述示例中,由于我们只关心user对象中的id,name和gender是否存在,其他属性不做明确规定,因此我们就可以使用Pick从User接口中拣选出我们关心的属性而忽略其他属性的编译检查。

8、never 永不存在

never表示的是那些永不存在的值的类型,比如在函数中抛出异常或者无限循环,never类型可以是任何类型的子类型,也可以赋值给任何类型,但是相反却没有一个类型可以作为never类型的子类型,示例如下:

// 函数抛出异常
function throwError(message: string): never {
    throw new Error(message);
}

// 函数自动推断出返回值为never类型
function reportError(message: string) {
    return throwError(message);
}

// 无限循环
function loop(): never {
    while(true) {
        console.log(1);
    }
}

// never类型可以是任何类型的子类型
let n: never;
let a: string = n;
let b: number = n;
let c: boolean = n;
let d: null = n;
let e: undefined = n;
let f: any = n;

// 任何类型都不能赋值给never类型
let a: string = '123';
let b: number = 0;
let c: boolean = true;
let d: null = null;
let e: undefined = undefined;
let f: any = [];

let n: never = a;
// -> Type 'string' is not assignable to type 'never'.

let n: never = b;
// -> Type 'number' is not assignable to type 'never'.

let n: never = c;
// -> Type 'true' is not assignable to type 'never'.

let n: never = d;
// -> Type 'null' is not assignable to type 'never'.

let n: never = e;
// -> Type 'undefined' is not assignable to type 'never'.

let n: never = f;
// -> Type 'any' is not assignable to type 'never'.

9、Exclude 属性排除

与Pick相反,Pick用于拣选出我们需要关心的属性,而Exclude用于排除掉我们不需要关心的属性,示例如下:

// 该类型已内置在TypeScript中
// 这里使用了条件类型(Conditional Type),和JS中的三目运算符效果一致
type Exclude<T, U> = T extends U ? never : T;

interface User {
    id: number;
    name: string;
    age: number;
    gender: number;
    email: string;
}

type keys = keyof User; // -> "id" | "name" | "age" | "gender" | "email"

type ExcludeUser = Exclude<keys, "age" | "email">;
// 等价于
type ExcludeUser = "id" | "name" | "gender";

在上述示例中我们通过在ExcludeUser中传入我们不需要关心的age和email属性,Exclude会帮助我们将不需要的属性进行剔除,留下的属性id,name和gender即为我们需要关心的属性。一般来说,Exclude很少单独使用,可以与其他类型配合实现更复杂更有用的功能。

10、Omit 属性忽略

在上一个用法中,我们使用Exclude来排除掉其他不需要的属性,但是在上述示例中的写法耦合度较高,当有其他类型也需要这样处理时,就必须再实现一遍相同的逻辑,不妨我们再进一步封装,隐藏这些底层的处理细节,只对外暴露简单的公共接口,示例如下:

// 使用Pick和Exclude组合实现
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

interface User {
    id: number;
    name: string;
    age: number;
    gender: number;
    email: string;
}

// 表示忽略掉User接口中的age和email属性
type OmitUser = Omit<User, "age" | "email">;
// 等价于
type OmitUser = {
  id: number;
  name: string;
  gender: number;
};

let user: OmitUser = {
    id: 1,
    name: 'tom',
    gender: 1
};

在上述示例中,我们需要忽略掉User接口中的age和email属性,则只需要将接口名和属性传入Omit即可,对于其他类型也是如此,大大提高了类型的可扩展能力,方便复用。

总结

在本文中总结了几种TypeScript的使用技巧,如果在我们的TypeScript项目中发现有很多类型声明的地方具有共性,那么不妨可以使用文中的几种技巧来对其进行优化改善,增加代码的可维护性和可复用性。

海计划公众号
(0)
上一篇 2020/03/23 01:36
下一篇 2020/03/23 01:36

您可能感兴趣的内容

  • 汇智网基础入门一个学习最前沿编程技术的平台

    汇智网基础入门 官方网址:http://www.hubwiz.com/ 简介描述:一个学习最前沿编程技术的平台

    2020/03/05
  • 在Linux 命令行中转换大小写小白常识_Linux菜鸟教程

    在Linux 命令行中转换大小写小白常识 有很多方法可以在 Linux 命令行中将文本从小写更改为大写,反之亦然。实际上,有一组这样的命令可以选择。这篇文章检验了一些优秀的命令来完…

    2020/03/20
  • jquery使用on()方法绑定的事件被执行多次的问题指南攻略_事件小白入门

    jQuery用on()方法绑定了事件之后,在代码执行过程中,可能会遇到事件被多次执行的情况。本来以为是事件冒泡的问题,后来发现是on()方法的特性引起的问题。简单还原一下问题的场景这里简单还原一下问题的场景,使用一个按钮给另一个按钮通过on()方法绑定事件。HTML的部分<input id="bindEventBtn" type="button" value

    2020/03/23
  • Js对象的三个属性小白基础_属性基础指南

    对象的三个属性每个对象都拥有三个特性(属性)(object attribute):对象的原型(prototype)。指向另外一个对象,本对象的属性继承自它的原型对象。对象的类(class)。是一个表示对象类型的字符串。对象的扩展标记(extensible flag)。指明了(在ES5中)是否可以向该对象添加新属性。原型属性【01】对象的原型属性是用来继承属性

    2020/03/29
  • rete基础入门_一款用于可视化编程和创建节点编辑器的Js框架

    rete基础入门 官方网址:https://rete.js.org GitHub:https://github.com/retejs/rete 简介描述:一款用于可视化编程和创建节…

    2020/03/06
  • 探索区块链的原理入门基础_区块链使用帮助

    之前经常听别人说,挖矿、比特币、区块链之类的东西,听起来好高大上呀,后来在网上了解相关知识,但是网上大部分都是转载,没一个能讲明白的,后来不知不觉接触到了一些易懂的点,所以今天整合一下,让大家彻彻底底的明白到底什么是区块链、什么是挖矿、什么是比特币。从头到尾都是小鹿三个小时的手动敲打,因为最近准备面试,写作的时间越来越少,但是我还是喜欢抽时间分享出来给各位读

    2020/03/30
  • dancer.js基础入门_一个高级的音频API

    dancer.js基础入门 官方网址:http://jsantell.github.com/dancer.js GitHub:https://github.com/jsantell…

    2020/03/06
  • 面试常问的28个Linux命令指南教程_linux小白入门

    面试常问的28个Linux命令指南教程 作为一个Java开发人员,有些常用的Linux命令必须掌握。即时平时开发过程中不使用Linux(Unix)或者mac系统,也需要熟练掌握Li…

    2020/03/20
  • JavaScript中的类型转换小白指南_类型指南教程

    正文之前,先抛出几组问题:// 第一组
    [] == [] //false// 第二组
    [] == ![] //true{} == !{} //false{} == ![] // false[] == !{} //true{} == 1 // false// 第三组
    {} 1 // false看到这几个问题,是不是一脸懵逼?稍

    2020/03/26
  • Js代理模式小白基础_模式入门指南

    所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网络连接、内存中的大对象、文件或其它昂贵或无法复制的资源。著名的代理模式例子为引用计数(英语:reference counting)指针对象。当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少内存用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂

    2020/03/26
  • 减少嵌套,降低代码复杂度菜鸟教程网_代码使用攻略

    这是我们’Code Health’系列的另一篇文章。这篇文章的一个版本最初出现在世界各地的谷歌浴室,作为一个谷歌的厕所事件测试。你可以下载一个友好的版本在你的办公室显示。 —— Elliott Karpilovsky 深度嵌套的代码会大大降低代码可读性,并且容易出错。请尝试在以下两个版本的代码中发现错误: 答案揭晓:代码中输出wrong encoding和

    2020/04/05
  • Flutter For Web基础指南_Flutter基础入门

    用来构建漂亮、定制化应用的跨平台的 UI 框架 Flutter 现在已经支持 Web 开发了。我们很高兴推出了一个预览版的 SDK 可以让开发者直接使用 Flutter UI 和业务逻辑代码构建 web 应用在浏览器中运行起来。Flutter 在 Web端的雄心自从去年第一个公测版本推出之后,开发者使用 Flutter 构建跨 IOS 和 Android 的

    2020/03/29
  • 微信小程序常用的几个UI组件库小白帮助_小程序小白教程

    1.WeUIWeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信 Web 开发量身设计,可以令用户的使用感知更加统一。包含button、cell、dialog、 progress、 toast、article、actionsheet、icon等各式元素。GitHub 地址:https://github.com/Tencent/weui

    2020/03/20
  • 高效的学习的几种方式入门基础知识_学习小白基础

    最近看了万维钢老师的《万万没想到》这本书感触很多。学习可以说是生活中的一部分,无论是为了升值加薪还是为了扩充自己的知识面,还是无意中的好奇心,我们都会去学习。不过很多时候,学过的东西没多久就忘记了,有人说在一个领域练习一万个小时就能成为这个领域的高手,可是你在某一行呆了很多年了依然菜的不行。怎么才能让自己的知识技能不断进步呢,当然就是学习,不过并不是学了就真

    2020/03/29
  • 什么是大数据?大数据的产生、特点、用途教程视频_大数据使用指南

    一.什么是大数据大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。大数据指不用随机分析法(抽样调查)这样捷径,而采用所有数据进行分析处理。大数据的5V特点(IBM提出):Volume(大量)、Velocity(高速)

    2020/03/23
  • typeahead.js指南教程jquery input 搜索自动补全jQuery插件_插件小白基础

    jquery.typeahead.js是一款高级的自动补全jQuery插件。该自动补全插件提供超过50个配置选项和回调方法,用于完成自动补全功能,能够完成绝大部分表单自动补全的需求。使用方法在页面中引入jquery、jquery.typeahead.min.js和jquery.typeahead.min.css文件。
    <link rel="styleshee

    Web前端 2020/04/05