您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > javascript

JavaScript 继承全解析

时间:2022-03-15 11:04:38  来源:  作者:陈璋写字的地方

JAVAScript 继承全解析(建议收藏)

在大多数的编程语言中,继承都是一个重要的主题。

JavaScript 继承全解析(建议收藏)

 

在那些基于类的编程语言中,继承提供了两个有用的服务。首先,它是代码重用的一种形式。如果一个新的类和一个已经存在的类大部分相似,那么你只须说明其不同点即可。

代码重用的模式极为重要,因为它们很有可能显著地减少软件开发的成本。类继承的另一个好处是它包括了一套类型系统的规范。由于程序员无需显式类型转换的代码,他们的工作量将大大的减轻,这是一件很好的事情,因为类型转换时会丢失类型系统在安全上的好处。

JavaScript 是一门弱类型语言,从不需要类型转换。对象的起源是无关紧要的,对于一个对象来说重要的是它能做什么,而不是它从哪里来。

JavaScript 提供了一套更为丰富的代码重用模式,它可以模拟那些基于类的模式,同时它也可以支持其他更具表现力的模式。在 JavaScript 可能的继承模式有很多。

在基于类的语言中,对象是类的实例,并且类可以从另一个类继承而来。JavaScript 是一门基于原型的语言,这意味着对象直接从其他对象继承。

原型可以直接从对象继承。

伪类

JavaScript 的原型存在着诸多的矛盾。某些看起来有点像基于类的语言的复杂语法问题遮蔽了它的原型机制。它不让对象直接从其他对象继承,反而插入了一个多余的间接层,从而使构造器函数产生对象。

当一个函数对象被创建时, Function 构造器产生的函数对象会运行类似这样一个代码:

this.prototype = {
    concstructor: this
}

新函数对象被赋予一个 prototype 属性,其值是包含一个 constructor 属性且属性值为该函数对象。该 prototype 对象是存放继承特征的地方。因为 JavaScript 语言没有提供一种方法去确定哪个函数是打算来做构造器的,所以每个函数都会得到一个 prototype 对象。constructor 属性没什么用。重要的是 prototype 对象。

当采用构造器调用模式,即使用 new 前缀去调用一个函数时,这将修改函数执行的方式。

如果 new 运算符是一个方法,而不是一个运算符,它可能会像这样执行。

if(typeof Object.beget !== 'function') {
    Object.beget = function(o) {
        var F = function() {}
        F.prototype = o
        return new F()
    }
}
Function.prototype.method = function (name, func) {
    if(!this.prototype[name]) {
        this.prototype[name] = func
        return this
    }
}
Function.method('new', function () {
    // 创建一个新对象,它继承自构造器函数的原型对象.
    var that = Object.beget(this.prototype)
    // 调用构造器函数,绑定 this 到新新对象。
    var other = this.Apply(that, arguments)
    // 如果它的返回值不是一个对象,就返回该对象。
    return (typeof other === 'object' && other) || that
})

我们可以定义一个构造器并扩充它的原型

var Mammal = function(name) {
    this.name = name
}
Mammal.prototype.get_name = function () {
    return this.name
}
Mammal.prototype.says = function () {
    return this.saying || ''
}

现在,我们可以构造一个实例

var myMammal = new Mammal('jeason')
var name = myMammal.get_name()

我们可以构造另一个伪类来继承 Mammal,这是通过定义它的 constructor 函数并替换它的 prototype 为一个 Mammal 的实例来实现的。

var Cat = function(name) {
    this.name = name;
    this.saying = 'meow';
}
// 替换 Cat.prototype 为一个新的 Mammal 实例
Cat.prototype = new Mammal()
Cat.prototype.purr = function(n) {
    return 'purr'
}
Cat.prototype.get_name = function() {
    return this.says() +  ' ' + this.name + ' ' + this.says()
}
var myCat = new Cat('foobar')
console.log(myCat.says())
console.log(myCat.purr(5))
console.log(myCat.get_name())
console.log(myCat)

伪类模式本意是想向面向对象靠拢,但它看起来格格不入。我们可以隐藏一些丑陋的细节,这是通过使用 method 方法定义一个 inherits 方法来实现的

Function.method('inherits', function (Parent) {
    this.prototype = new Parent()
    return this
})

我们的 inherits 和 method 方法都返回 this,这将允许我们可以以级联的样式编程。

var BigCat = function(name) {
    this.name = name;
    this.saying = 'meow';
}
BigCat.inherits(Mammal).
    method('purr', function (n) {
        return 'purr'
    }).
    method('get_name', function () {
        return this.says() +  ' ' + this.name + ' ' + this.says()
    });
console.log(BigCat)
var myCat1 = new BigCat('foobar')
console.log(myCat1.says())
console.log(myCat1.purr(5))
console.log(myCat1.get_name())
console.log(myCat1)

通过隐藏那些「无谓的」 prototype 操作细节,现在它看起来没那么怪异了。但我们是否真的有所改善呢?我们现在有了行为像「类」的构造器函数,但仔细去看,它们可能有着令人惊讶的行为:没有私有环境,所有属性都是公开的。无法访问父类的方法。

更糟糕的是,使用构造器函数存在一个严重的危害。如果你在调用构造器函数时忘记在前面加上 new 前缀,那么 this 将不会被绑定到一个新对象上。可悲的是, this 将被绑定到全局对象上,所以你不但没有扩充新对象,反而破坏了全局变量。这真的是糟糕透顶,发生了那样的情况时,既没有编译时警告,也没有运行时警告。

这是一个严重的语言设计错误。为了降低这个问题带来的风险,所有的构造器函数都约定命名成首字母大写的形式,并且不以首字母大写的形式拼写任何其他的东西。这样我们至少可以通过眼睛检查是否缺少了 new 前缀。一个更好的备选方案就是根本不使用 new。

「伪类」可以给不熟悉 JavaScript 的程序员提供便利,但它也隐藏了该语言的真实本质。借鉴类的表示法可能误导程序员编写过于深入与复杂的层次结构。许多复杂的类层次结构产生的原因就是静态类型检查的约束。JavaScript 完全摆脱了那些约束,在基于类的语言中,类的继承是代码重用的唯一方式,JavaScript 有着更多且更好的选择。

对象说明符

有时候,构造器要接受一大串参数,这可能是令人烦恼的,因为要记住参数的顺序可能非常困难。在这种情况下,如果我们编写构造器时介绍一个简单的对象说明符可能更加友好。那个对象包含了将要构建的对象规格说明,所以,与其这么写:

var obj1 = marker(arg1, arg2, args3)

不如这么写:

var obje2 = marker({
    first: arg1,
    setcond: arg2,
    third: arg3
})

现在多个参数可以按照任意顺序排列,如果构造器聪明地使用默认值,一些参数可以忽略掉,并且代码也更容易阅读。

当与 JSON 一起工作时,这还可以有一个间接的好处。JSON 文本只能描述数据,但有时数据表示的一个对象,将该数据与它的方法关联起来是有用的。如果构造器取得一个对象说明符,可以容易做到,因为我们可以简单传递该 JSON 对象构造器,而它将返回一个构造完全的对象。

原型

在一个纯粹的原型模式中,我们会摒弃类,转而专注于对象。基于原型的继承相比基于类的继承的概念上更为简单:一个新对象可以继承一个旧对象的属性。也许你对此感到陌生,但它真的很容易理解。你通过构造一个有用的对象开始,接着可以构造更多和那个对象类似的对象。可以完全避免把一个应用拆解成一系列嵌套抽象类的分类过程。

让我们先用对象字面量去构造一个有用的对象。

var myMammal = {
    name: 'foobar',
    get_name: function() {
        return this.name
    },
    says: function () {
        return this.saying || ''
    }
}

一旦有了一个对象,我们就可以利用 Object.beget 方法构造出更多的实例出来,接下来我们要定制新的实例:

var myMammal = {
    name: 'foobar',
    get_name: function() {
        return this.name
    },
    says: function () {
        return this.saying || ''
    }
}
var myCat2 = Object.beget(myMammal)
myCat2.name = 'foo bar Jeason'
mycat2.saying = 'meow'
mycat2.purr = function() {
    return 'purr'
}
mycat2.get_name = function () {
    return this.syas + ' ' + this.name + ' ' + this.says
}

这是一种「差异化继承」,通过定制一个新的对象,我们指明了它与所基于的基本对象的区别。

有时候,它对某种数据结构从其他数据结构继承的情形非常有用。这里就有一个例子:假定我们要解析一门类似 JavaScript 那样一对用花括号指示作用域的语言。定义在一个作用域中的条目在该作用域之外是不可见的。从某种意义来理解,也就是说一个内部作用域会继承它的外部作用域。JavaScript 在表示这样的关系上做得非常好。当遇到一个花括号时 block 函数将从 scope 中寻找符号,并且当它定义了新的符号时扩充 scope:

var block = function () {
    // 记住当前的作用域,构造了一个包含了当前作用域中所有的对象的新作用域
    var oldScope = scope;
    scope = Object.beget(scope)
    // 传递花括号作为参数调用 advance 
    advance("{")
    // 使用新的作用域进行解析
    parse(scope)
    // 传递右花括号作为参数调用 advance 并抛弃新作用域,恢复原来老的作用域
    advance("}")
    scope = oldScope
}

函数化

迄今为止,所看到的继承模式的一个弱点就是我们没法保护隐私。对象的所有属性都是可见的。我们没法得到私有变量和私有函数。有时候那不要紧,但有时候却是大麻烦。遇到这些麻烦的时候,一些不知情的程序员接受了一种伪装私有的模式。如果想构造一个私有的属性,他们就给其起一个怪模怪样的名字,并且希望其他使用代码的用户假装看不到这些奇怪的成员元素。幸运的是,我们有一个更好的选择,那就是模块模式的应用。

我们从构造一个将产生对象的函数开始,给它起的名字将以一个小写字母开头,因为它并不需要使用 new 前缀。该函数包括四个步骤。

1.它创建了一个新对象。有很多的方式去构造一个对象。它可以构造一个对象字面量,或者它可以和 new 前缀连用去调用一个构造器函数,或者它可以使用 Object.beget 方法去构造一个已经存在的新对象的实例,或者它可以调用任意一个会返回一个对象的函数。2.它选择性地定义私有实例变量和方法。这些就是函数中通过 var 语句定义的普通变量。3.它给这个新对象扩充方法。那些方法将拥有特权去访问参数,以及在第二步中通过 var 语句定义的变量。4.它返回那个新对象。

这里是一个函数构造器的伪代码版本

var constructor = function(spec, my) {
    var foo, bar
    my = my || {}
    // 把共享的变量和函数添加到 my 中
    that = new object
    // 添加给 that 的特权方法
    that.test = function() {}
    return that
}

spec 对象包含构造器需要构造一个新实例的所有信息。spec 的内容可能会被复制到私有变量中,或者被其他函数改变。或者方法可以在需要的时候访问 spec 的信息。

my 对象是一个继承中的构造器提供秘密共享的容器。my 对象可以选择性地使用,如果没有传入一个 my 对象,那么会创建一个 my 对象。

接下来,声明该对象私有的实例变量和方法。通过简单的声明变量就可以做到。构造器变量和内部函数变成了该实例的私有成员。内部函数可以访问 spec、my、that,以及其他私有变量。

接下来,给 my 对象添加共享的秘密成员。这是通过赋值语句来做的:

my.member = value

现在,我们构造了一个新对象并将其赋值给 that。有很多方式可以构造一个新对象,我们可以使用对象字面量,可以用 new 运算符调用一个伪类构造器,传给它一个 spec 对象和 my 对象。my 对象允许其他的构造器分享我们放到 my 中的资料。其他的构造器可能也会将自己的可分享的秘密成员放进 my 对象中,以便我们的构造器可以使用它。

接下来,我们扩充 that,加入组成该对象接口的特权方法,我们可以分配一个新函数成为 that 的成员方法,或者,更安全的,我们可以先将函数定义为私有方法,然后再将它们分配给 that:

var methdical = function () {}
that.methdiccal = methdical

分两步去定义 methdical 的好处是,如果其他方法想要调用 methdical,它们可以直接调用 methdical(),而不是 that.methdical()。如果该实例被破坏或篡改,甚至 that.methdical 被替换掉了,调用 methdical 的方法将同样会继续工作,因为它们私有的 methdical 不受该实例的影响。

最后,我们返回 that

让我们这个模式应用到 mammal 例子里,此处不需要 my,所以我们先抛开它,但将使用一个 spec 对象。

name 和 saying 属性现在是完全私有的,它们只有通过 get_name 和 says 两个特权方法才可以访问。

var mammal = function(spec) {
    var that = {}
    that.get_name = function() {
        return spec.name
    }
    that.says = function() {
        return spec.saying || ''
    }
    return that
}
var myMammal = mammal({
    name: 'foo bar'
})

在伪类模式中,构造器函数 Cat 不得不重复构造器 Mammal 已经完成的工作。在函数化模式中那不再需要了,因为构造器 Cat 将会调用构造器 Mammal,让 Mammal 去做对象创建中的大部分工作,所以 Cat 只需要关注自身的差异即可。

var cat = function(spec) {
    spec.saying = spec.saying || 'meow'
    var that = mammal(spec)
    that.purr = function () {
        return 'purr'
    }
    that.get_name = function() {
        return that.says() + ' ' + spec.name + ' '  + that.says()
    }
    return that
}
var myCat = cat({name: 'jeaosn'})
console.log(myCat)

函数化模式还给我们提供了一个处理父类方法的方法,我们将构造一个 superior 方法,它取得一个方法名并返回调用那个方法的函数。该函数将调用原来的那个方法,尽管属性已经变化了。

Object.method('superior', function(name) {
    var that = this
    var method = that[name]
    return function() {
        return method.apply(that,arguments)
    }
})

让我们在 coolcat 上试验一下, coolcat 就像是 cat 一样,除了它有一个更酷的调用父类方法的 get_name 方法。它只需要一点点准备工作,我们将声明一个 super_get_name 变量,并且把调用 superior 方法所返回的结果赋值给它。

var coolcat = function(spec) {
    var that = cat(spec)
    var super_get_name = that.superior('get_name');
    that.get_name = function() {
        return 'like' + ' ' + super_get_name() + 'baby'
    }
    return that
}
var myCoolCat = coolcat({name: 'josh'})
var name= myCoolCat.get_name()
console.log(name)
console.log(myCoolCat)

函数化模式有很大的灵活性,它不仅不像伪类模式那样需要很多功夫,还让我们得到更好的封装和信息隐藏,以及访问父类方法的能力。

如果对象的所有状态都是私有的,那么该对象就成为一个「防伪」对象。该对象的属性可以被替换或删除,但该对象的完整性不会受到损害。如果我们用函数化的样式创建一个对象,并且该对象的所有方法都不使用 this 或 that,那么该对象就是持久性的。一个持久性对象就是一个简单功能函数的集合。

一个持久性对象不会被损害,访问一个持久性对象时,除非被方法授权,否则攻击者不能访问对象的内部状态。

部件

我们可以从一套部件中组合出对象来。例如,我们可以构造一个添加简单事件处理特性到任何对象上的函数。它会给对象添加一个 on 方法,一个 fire 方法和一个私有的事件注册对象。

var eventuality = function(that) {
    var registry = {}
    that.fire = function () {
        // 在一个对象是触发一个事件,该事件可以是一个包含事件名称的字符串
        // 或是拥有一个包含事件名称 type 属性的对象
        // 通过 ‘on' 方法注册的事件处理程序中匹配事件名称的函数将被调用
        var array
        var func
        var handle
        var i
        var type = typeof event === 'string' ? event : event.type
        // 如果这个事件存在一组事件处理程序,那么就遍历它们并顺序依次执行
        if (registry.hasOwnProperty(type)) {
            array = registry[type];
            for(i = 0; i< array.lenth;i += 1) {
                handle = array[i]
            }
            // 每组处理程序包含一个方法和一个可选的参数
            // 如果该方法是一个字符串形式的名字,那么寻找到该函数
            func =handle.method
            if (typeof func === 'string') {
                func =this[func]
            }
            // 调用一个处理程序,如果该条目包含参数,那么传递它们进去。否则,传递该事件对象
            func.apply(this, handle.parameters || [event]);
        }
        return this
    }
    that.on = function(type, method, parameters) {
        // 注册一个事件,构造一条处理程序条目,将它插入到处理程序数组中
        // 如果这种类型还不存在,那就构造一个
        var handler = {
            method: method,
            parameters: parameters
        }
        if(registry.hasOwnProperty(type)) {
            registry[type].push(handler)
        } else {
            registry[type] = [handler]
        }
        return this
    }
    return that
}

我们可以在任何单独的对象上调用 eventuality,授予它事件处理方法。我们也可以赶在 that 被返回之前在一个构造器函数中调用它。

eventuality(that)

用这种方式,一个构造器函数可以从一套部件中组装出对象。JavaScript 的弱类型在此处是一个巨大的优势,因为我们无需花费精力去关注一个类型系统的类族谱。相反,我们专注它的个性内容。



Tags:JavaScript   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  Search: JavaScript  点击:(6)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践&mdash;&mdash;如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  Search: JavaScript  点击:(29)  评论:(0)  加入收藏
使用 JavaScript 清理我的 200GB iCloud,有了一个意外发现!
本文作者在综合成本因素之下,决定用 Java 脚本来清理一下自己的 iCloud,结果却有了一个意外发现,即在 iCloud 中上传同一个视频和删除此视频之后,iCloud 的空间并不一致,这到底是...【详细内容】
2024-01-11  Search: JavaScript  点击:(106)  评论:(0)  加入收藏
JavaScript 真的是在 10 天内完成的吗?
起初网景公司用了十天时间创建了一个 Java,后来它成为事实上的 Web 标准,并横扫各大编程语言榜单成为开发者最受欢迎的语言之一。近日,有开发者对 Java 的开发周期提出了质疑,以...【详细内容】
2024-01-03  Search: JavaScript  点击:(88)  评论:(0)  加入收藏
创建一个双模式跨运行时的 JavaScript 包,你学会了吗
本文将指导你发布双模式、跨运行时的 JavaScript 包。了解如何创建与 ESM 和 CommonJS 以及 Node.js、Deno 和浏览器等不同运行时兼容的库。随着 JavaScript 开发的不断发展...【详细内容】
2023-12-27  Search: JavaScript  点击:(166)  评论:(0)  加入收藏
五种在 JavaScript 中创建对象的方法
在 JavaScript 中,对象是多功能工具,可以通过多种方式创建,每种方式适合不同的场景。了解何时使用每种方法是编写高效且可维护的 JavaScript 代码的关键。让我们探讨在 JavaScr...【详细内容】
2023-11-23  Search: JavaScript  点击:(245)  评论:(0)  加入收藏
GitHub:程序员正积极使用 AI 编程、JavaScript 语言依然最流行
IT之家 11 月 20 日消息,GitHub 发布了 2023 年度 Octoverse 开源状态报告,其中主要强调了 AI 在开发过程中的作用,并围绕云和 Git 的开源活动展开。官方介绍称,今年的三大趋势...【详细内容】
2023-11-20  Search: JavaScript  点击:(173)  评论:(0)  加入收藏
通过示例解释所有 JavaScript 数组方法
作为一名程序员,我们的工作是写有效的代码,但是仅仅写有效的代码,这还不够。如果想成为优秀的程序员,我们还需要编写可维护和可扩展的代码。JavaScript为我们提供了很多可以用来...【详细内容】
2023-11-15  Search: JavaScript  点击:(266)  评论:(0)  加入收藏
JavaScript 地位不保!WasmGC 将成为下一个“网红”?
整理 | 太冷不穿格子衫 出品 | 51CTO技术栈(微信号:blog51cto)早在 2017 年,主流浏览器都已经支持 WebAssembly。随着 WebAssembly 的蓬勃发展,各种编程语言也在增加对它的支持。...【详细内容】
2023-11-13  Search: JavaScript  点击:(218)  评论:(0)  加入收藏
Jest:目前最广泛使用的前端 JavaScript 测试框架
Jest 是一个简单易用的 JavaScript 测试框架。最初由 Meta 公司团队维护。2022 年 5 月,Meta 公司正式将自己的开源项目Jest 移交给 OpenJS Foundation[1],这表示 Jest 由公司...【详细内容】
2023-11-08  Search: JavaScript  点击:(342)  评论:(0)  加入收藏
▌简易百科推荐
JavaScript的异步编程常见模式
在JavaScript中,异步编程是一种处理长时间运行操作(如网络请求或I/O操作)的常见方式。它允许程序在等待这些操作完成时继续执行其他任务,从而提高应用程序的响应性和性能。JavaS...【详细内容】
2024-04-12  靳国梁    Tags:JavaScript   点击:(6)  评论:(0)  加入收藏
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  前端新世界  微信公众号  Tags:JavaScript   点击:(6)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践&mdash;&mdash;如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  前端新世界  微信公众号  Tags:JavaScript   点击:(29)  评论:(0)  加入收藏
又出新JS运行时了!JS运行时大盘点
Node.js是基于Google V8引擎的JavaScript运行时,以非阻塞I/O和事件驱动架构为特色,实现全栈开发。它跨平台且拥有丰富的生态系统,但也面临安全性、TypeScript支持和性能等挑战...【详细内容】
2024-03-21  前端充电宝  微信公众号  Tags:JS   点击:(28)  评论:(0)  加入收藏
构建一个通用灵活的JavaScript插件系统?看完你也会!
在软件开发中,插件系统为应用程序提供了巨大的灵活性和可扩展性。它们允许开发者在不修改核心代码的情况下扩展和定制应用程序的功能。本文将详细介绍如何构建一个灵活的Java...【详细内容】
2024-03-20  前端历险记  微信公众号  Tags:JavaScript   点击:(23)  评论:(0)  加入收藏
对JavaScript代码压缩有什么好处?
对JavaScript代码进行压缩主要带来以下好处: 减小文件大小:通过移除代码中的空白符、换行符、注释,以及缩短变量名等方式,可以显著减小JavaScript文件的大小。这有助于减少网页...【详细内容】
2024-03-13  WangLiwen    Tags:JavaScript   点击:(5)  评论:(0)  加入收藏
跨端轻量JavaScript引擎的实现与探索
一、JavaScript 1.JavaScript语言JavaScript是ECMAScript的实现,由ECMA 39(欧洲计算机制造商协会39号技术委员会)负责制定ECMAScript标准。ECMAScript发展史: 2.JavaScript...【详细内容】
2024-03-12  京东云开发者    Tags:JavaScript   点击:(6)  评论:(0)  加入收藏
面向AI工程的五大JavaScript工具
令许多人惊讶的是,一向在Web开发领域中大放异彩的JavaScript在开发使用大语言模型(LLM)的应用程序方面同样大有价值。我们在本文中将介绍面向AI工程的五大工具,并为希望将LLM...【详细内容】
2024-02-06    51CTO  Tags:JavaScript   点击:(55)  评论:(0)  加入收藏
JS小知识,使用这6个小技巧,避免过多的使用 if 语句
最近在重构我的代码时,我注意到早期的代码使用了太多的 if 语句,达到了我以前从未见过的程度。这就是为什么我认为分享这些可以帮助我们避免使用过多 if 语句的简单技巧很重要...【详细内容】
2024-01-30  前端达人  今日头条  Tags:JS   点击:(60)  评论:(0)  加入收藏
18个JavaScript技巧:编写简洁高效的代码
本文翻译自 18 JavaScript Tips : You Should Know for Clean and Efficient Code,作者:Shefali, 略有删改。在这篇文章中,我将分享18个JavaScript技巧,以及一些你应该知道的示例...【详细内容】
2024-01-30  南城大前端  微信公众号  Tags:JavaScript   点击:(76)  评论:(0)  加入收藏
站内最新
站内热门
站内头条