JavaScript设计模式之英雄联盟

Souleigh ✨ 等级 610 0 0

JavaScript设计模式之英雄联盟

作者:黄梵高

原文: https://juejin.cn/post/6844904165982879758

构造函数模式

简介

在JavaScript里,构造函数通常是认为用来实现实例的特殊的构造函数。通过new关键字来调用定义的构造函数,你可以告诉JavaScript你要创建一个新对象并且新对象的成员声明都是构造函数里定义的。在构造函数内部,this关键字引用的是新创建的对象。

作为一个老联盟fans,一定要亲手实现一下设计模式也可以融会贯通。

现在打算创建一个英雄联盟对象,需要地图,英雄,士兵,野怪,还有开始游戏的按钮。

JavaScript设计模式之英雄联盟

function LOL(maps, heros, soldier, monster) {  
    this.maps = maps  
    this.heros = heros  
    this.soldier = soldier  
    this.monster = monster  
    this.start = function() {  
        return '地图:' + this.maps + '\n对战英雄:' + this.heros.join() + '\n小兵类型:' + this.soldier + '\n野怪:' + this.monster + '\n'  
    }  
}  


var game1 = new LOL('召唤师峡谷', ['影流之主', '诡术妖姬'], '超级兵', '红buff')  
var game2 = new LOL('大乱斗', ['影流之主', '诡术妖姬'], '超级兵', '红buff')  
console.log(game1.start())  
console.log(game2.start())  


这样写代码,每局游戏需要重新创建一个英雄联盟实例,

这样使用构造器,有多少个game就需要多少个start函数方法,如果共用一个start方法,可以节约很多内存

function LOL(maps, heros, soldier, monster) {  
    this.maps = maps  
    this.heros = heros  
    this.soldier = soldier  
    this.monster = monster  
}  
LOL.prototype.start = function() {  
   return '地图:' + this.maps + '\n对战英雄:' + this.heros.join() + '\n小兵类型:' + this.soldier + '\n野怪:' + this.monster + '\n'  
}  


var game1 = new LOL('召唤师峡谷', ['影流之主', '诡术妖姬'], '超级兵', '红buff')  
var game2 = new LOL('大乱斗', ['影流之主', '诡术妖姬'], '超级兵', '红buff')  

console.log(game1.start())  
console.log(game2.start())  

如果让start方法变成大家通用的就好了,因此把LOL.prototype.start改写,这样所以的LOL实例就可以共用一个方法,从原型链上继承即可

上面的方式可以节省内存,start实例函数可以在所有LOL对象的实例中使用

如果不使用new 也可以有其他方式创建对象

function LOL(maps, heros, soldier, monster) {  
    this.maps = maps  
    this.heros = heros  
    this.soldier = soldier  
    this.monster = monster  
    this.start = function() {  
        return '地图:' + this.maps + '\n对战英雄:' + this.heros.join() + '\n小兵类型:' + this.soldier + '\n野怪:' + this.monster + '\n'  
    }  
}  


var game3 = new Object();  
LOL.call(game3, "扭曲丛林", ['影流之主', '剑圣'], '远程兵', '大龙');  
console.log(game3.start())  

//也可以不使用new ,通过call方法在game3的作用域调用LOL  


这种方式虽然可以创建新的构造函数,但却不能继承LOL原型上的函数

如果直接运行LOL()函数(不使用new的情况下),由于this指向的是window对象,因此start方法会变成window.start()

如果强制要求函数使用new 方法也可以如下创建:

function LOL(maps, heros, soldier, monster) {  
    if (!(this instanceof LOL)) {  
        return new LOL(maps, heros, soldier, monster);  
    }  
    this.maps = maps  
    this.heros = heros  
    this.soldier = soldier  
    this.monster = monster  
}  

通过判断this的instanceof,就可以知道究竟是来自new方法,还是说是直接调用。如果是直接调用的话,判断条件为true,还是会return一个新的实例。

e.g:

var s = new String("lol");  
var n = new Number(101);  
var b = new Boolean(true);  
// s n b返回的是实例对象   
//String{  
// 0: 'l',  
// 1: 'o',  
// 2: 'l'  
// }  

//可以直接给变量赋值,或者不加new 关键词  

外观模式

简介

外观模式最大的体现其实就是入口,比如init()函数,把一些内部的函数都放在这个门面之下,只需要调用这个门面函数,其他乱七八糟的功能都可以实现。

现在有一个英雄,叫做亚索,我希望给他一些配置,比如技能,衣服等

JavaScript设计模式之英雄联盟

function YaSuo() {  
}  
function Qskill (hero) {  
    hero.prototype.Qskill = function() {  
        console.log('hasaki!!')  
    }  
}  
function Wskill (hero) {  
    hero.prototype.Wskill = function() {  
        console.log('风墙')  
    }  
}  
function Eskill (hero) {  
    hero.prototype.Eskill = function() {  
        console.log('快乐')  
    }  
}  
function Rskill (hero) {  
    hero.prototype.Rskill = function() {  
        console.log('痛里唉该痛')  
    }  
}  
function Skin (hero, skin) {  
    hero.prototype.skin = skin  
}  

function CreateYasuo () {  
    Qskill(YaSuo)  
    Wskill(YaSuo)  
    Eskill(YaSuo)  
    Rskill(YaSuo)  
    Skin(YaSuo, 'originSkin')  
    return new YaSuo()  
}  
CreateYasuo()  
// 创建成功 外观模式启动  

通过上面的代码,成功创建了一个美丽的亚索。我们最后只需要了解,外观模式不仅简化类中的接口,而且对接口与调用者也进行了解耦。外观模式经常被认为开发者必备,它可以将一些复杂操作封装起来,并创建一个简单的接口用于调用

说白了就是用一个接口封装其它的接口。

外观模式优点就是易使用。缺点则是,当连续使用外观模式创建的接口时,可能会产生性能问题。

e.g.

var addMyEvent = function (el, ev, fn) {  
    if (el.addEventListener) {  
        el.addEventListener(ev, fn, false);  
    } else if (el.attachEvent) {  
        el.attachEvent('on' + ev, fn);  
    } else {  
        el['on' + ev] = fn;  
    }  
};   

这是最常见对监听事件的处理,前端必会。其中的addMyEvent就是对其他三个接口的封装,产生了一个门面,也就是外观模式。

代理模式

简介

其实代理模式我们生活中接触的很多了。比如es6中的proxy对象,还有我们平时上网用的VPN。那其实代理模式,就是让一个对象帮助其他的对象来做事。

比如我现在想创建一个英雄,名字叫做卡莉斯塔,俗称滑板鞋。这个英雄有个特点,当她放R技能的时候,会把一个对象拉过来到自己身边几秒,代理这个对象的走路行为,禁止他释放技能等等,那这就要用到代理模式了。

// 声明走路动作  
function Walk (hero) { // 代理期间执行的操作  
    return function() {console.log(hero + ' is walk')}  
}  
function Kalisita () { // proxy  
    this.walk = Walk('Kalisita')  
    this.Rskill = function(hero) { // 传入要拉取的英雄  
        this.walk = function() {  
            Walk('Kalisita')() // 既需要自己走  
            hero.walk() // 还需要带着人一起走  
        }  
    }  
}  
function HeroA () { // 被代理走路的英雄  
    this.walk = Walk('heroA')  
}  

var k = new Kalisita()  
var a = new HeroA()  

k.walk() // Kalisita is walk  
a.walk() // heroA is walk  

k.Rskill(a) // k把a的walk事件代理了, 现在k触发walk的同时,也会带着a一起walk哦  
k.walk()  
// Kalisita is walk   
// heroA is walk  


代理模式主要用于几点,

  • 要获取本来没有的对对象操作的权限

  • 要获取远程文件,需要代理模式作为跳板

  • 根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象,比如浏览器的渲染的时候先显示问题,而图片可以慢慢显示(就是通过虚拟代理代替了真实的图片,此时虚拟代理保存了真实图片的路径和尺寸。

  • 只当调用真实的对象时,代理处理另外一些事情。例如C#里的垃圾回收,使用对象的时候会有引用次数,如果对象没有引用了,GC就可以回收它了。

详情可以参考《大话设计模式》  

而我们前端代码中用的比较多的,应该就是vue.js中对data中数据响应式的代理。vue3中也将使用大量ES6支持的Proxy对象来改写。

e.g.

通过代理,尝试设置私有属性

function getPrivateProps(obj, filterFunc) {  
  return new Proxy(obj, {  
    get(obj, prop) {  
      if (!filterFunc(prop)) {  
        let value = Reflect.get(obj, prop);  
        // 如果是方法, 将this指向修改原对象  
        if (typeof value === 'function') {  
          value = value.bind(obj);  
        }  
        return value;  
      }  
    },  
    set(obj, prop, value) {  
      if (filterFunc(prop)) {  
        throw new TypeError(`Cant set property ${prop}`);  
      }  
      return Reflect.set(obj, prop, value);  
    },  
    has(obj, prop) {  
      return filterFunc(prop) ? false : Reflect.has(obj, prop);  
    },  
    ownKeys(obj) {  
      return Reflect.ownKeys(obj).filter(prop => !filterFunc(prop));  
    },  
    getOwnPropertyDescriptor(obj, prop) {  
      return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop);  
    }  
  });  
}  

function propFilter(prop) {  
  return prop.indexOf('_') === 0;  
}  

策略模式

JavaScript设计模式之英雄联盟

简介

策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。

那听起来云山雾绕,怎么都涉及到 算法 了 ?难道我一个前端是时候进攻算法大军了吗。其实并不是,用一个超级常见的例子就可以解释!

让我们又回到英雄联盟,当我们第一次登陆英雄联盟的时候,需要输入一个新的姓名吧?起名规则起码得有以下这几条:

  • 名字长度

  • 名字是否有非法字符

  • 是否重名

  • 不能为空

其中具体的设定,只有开发者才知道了,身为玩家只能注意到这几点,那策略模式怎么体现在这里的呢?首先我们实现一个显而易见功能的例子:

var validator = {  
    validate: function (value, type) {  
        switch (type) {  
            case 'isNonEmpty ':  
                {  
                    return true; // 名字不能为空  
                }  
            case 'isNoNumber ':  
                {  
                    return true; // 名字 不是 纯数字  
                    break;  
                }  
            case 'isExist ':  
                {  
                    return true; // 名字已存在  
                }  
            case 'isLength':  
                {  
                    return true; // 长度合理  
                }  
        }  
    }  
};  

上述代码可以实现一个表单验证系统,刚创建角色起名字的时候验证那里的功能,只需要传入相应的参数就可以。

validator.validate('测试名字', 'isNumber') // false  

虽然可以得到理想的结果,但这种写法有十分严重的缺点,最重要的,每次增加或修改规则时,需要修改整个validate函数,这不符合开放封闭原则,增加逻辑,让函数更加复杂不可控。

那真正适合的代码应该怎么写呢?

var validator = {  
    // 所有验证规则处理函数存放的地方  
    types: {},  
    validate: function (str, types) {  
        this.messages = [];  
        var checker, result, msg, i;  
        for (i in types) {  
            var type = types[i];  
            checker = this.types[type]; // 获取验证规则的验证类  
            if (!checker) { // 如果验证规则类不存在,抛出异常  
                throw {  
                    name: "ValidationError",  
                    message: "No handler to validate type " + type  
                };  
            }  

            result = checker.validate(str); // 使用查到到的单个验证类进行验证  
            if (!result) {  
                msg = "Invalid value for *" + type + "*, " + checker.instructions;  
                this.messages.push(msg);  
            }  
        }  

        return this.hasErrors();  
    },  

    // 是否有message错误信息  
    hasErrors: function () {  
        return this.messages.length !== 0;  
    }  
};  

上面的代码定义了validator对象以及validate函数,函数内部会对传入的字符串,检测类型数组进行处理。如果存在规则,进行判断,并把错误信息发送到this.message。如果不存在规则,自然的就不需要继续执行,抛出error即可。

// 验证给定的值是否不为空  
validator.types.isNonEmpty = {  
    validate: function (value) {  
        return value !== "";  
    },  
    instructions: "传入的值不能为空"  
};  

// 验证给定的值是否 不是 纯数字  
validator.types.isNoNumber = {  
    validate: function (value) {  
        return isNaN(value); // 伪写法,因为isNaN会误判布尔值和空字符串等,因此并不能作为真正判断纯数字的依据  
    },  
    instructions: "传入的值不能是纯数字"  
};  

// 验证给定的值是否存在  
validator.types.isExist = {  
    validate: function (value) {  
        // $.ajax() ...  
        return true;  
    },  
    instructions: "给定的值已经存在"  
};  

// 验证给定的值长度是否合理  
validator.types.isLength = {  
    validate: function (value) {  
        var l = value.toString().length  
        if ( l > 2 && l < 10) {  
            return true;  
        } else {  
            return false;  
        }  
    },  
    instructions: "长度不合理,请长度在2-10个字符内"  
};  

上面对types规则进行了补充,定义了几种规则,至此,对于名称校验,简单的设定就敲完了。接下来要准备的就是一个能够在英雄联盟合理的名字进行验证:

var types = ['isExist', 'isLength', 'isNoNumber', 'isNonEmpty']; // 决定想要的规则,无论增加或者减少,原函数都不需要改动  
function check (name, types) {  
    validator.validate(name, types);  
    if (validator.hasErrors()) {  
        console.log(validator.messages.join("\n"));  
    } else {  
        console.log('验证通过!')  
    }  
}  
check('okckokckokck', types) // 长度不合理,请长度在2-10个字符内  
check('老faker', types) // true  
check('00001', types) // 传入的值不能是纯数字  

首先设定好想要的规则,用一个types数组囊括进来,之后定义一个check函数,把结果处理封装一下,最后传入参数,无论想要检测什么规则,都不需要修改原函数。现在无论我想检测faker可不可以注册,还是一个空字符串,都可以传入规则,进行使用。如果想添加新的规则,只需要在validator.types上续写对象就可以,方便清晰,结构明朗。

核心思想就是把复杂的算法结构,分别封装起来,让他们之间可以互相替换,上面的代码就很好的体现了 互相替换,因为无论我怎么去修改想要的规则,都不需要改动原本的代码。

桥接模式

JavaScript设计模式之英雄联盟

简介

在系统沿着多个维度变化的同时,又不增加其复杂度并已达到解耦。将抽象部分与它的实现部分分离,使它们都可以独立地变化。简单的说:桥接模式最主要的特点是实现层(如元素绑定的事件)与抽象层(如修饰页面UI逻辑)解耦分离

下面依然是一个例子:

假如我们还在英雄联盟的世界里,每一场游戏最终都会有一个结局,无论胜利还是失败,都会弹出一个窗口,告诉你 —— Victory或者是Defeat

function GameMessage (type) { // 抽象 与 实现 的 桥梁  
    this.fn = type ? new Victory() : new Defeat()  
}  
GameMessage.prototype.show = function() {  
    this.fn.show()  
}  

function Defeat() { // 抽象层  
    this.show = function() {  
        console.log('im loser')  
    }  
}  

function Victory() { // 抽象层  
    this.show = function() {  
        console.log('im winner')  
    }  
}  

// 实现层  
function getResult() {  
    var switchVD = Math.ceil(Math.random()*10) > 5 // 胜利失败一半一半  
    return new GameMessage(switchVD)  
}  
var result1 = getResult()  
var result2 = getResult()  
var result3 = getResult()  
result1.show()  
result2.show()  
result3.show()  

首先我们创建了一个GameMessage的函数,我们都知道胜利失败都有一半的概率,因此定义了switchVD变量,模拟一个随机事件,同时每次结果调用一次getResult函数,获取最新结果。

桥接模式体现在GameMessage函数上,将抽象的 Victory() 以及 Defeat() 与 我们获取结果的 getResult()实现解耦。函数之间不糅合逻辑,但又通过桥梁函数,连接在一起。

这么写的好处就是,两者都可以独立的变化,互不打扰。毕竟如果揉在一起,可能逻辑如下:

function Defeat() { // 抽象层  
    this.show = function() {  
        console.log('im loser')  
    }  
}  

function Victory() { // 抽象层  
    this.show = function() {  
        console.log('im winner')  
    }  
}  

var switchVD = Math.ceil(Math.random()*10) > 5  
if (switchVD) {  
    var result =  new Victory()  
} else {  
    var result =  new Defeat()  
}  

result.show() // loser or winner  

上述代码可以轻松的看到,如果没有桥接模式,直接把实现层,渲染层糅合在一起,会依赖上下文。倘若获取不到上下文的环境,很容易出现问题。

小结

桥接模式在日常开发中,会在不经意间频繁使用,目的也是为了让代码结构清晰,将不同逻辑的代码互相解耦。便于日后维护,开发时也更能区分模块,看的舒服,自然效率也高。

桥接模式关键是要理解抽象部分与实现部分的分离,使得二者可以独立的变化,而不必拘泥于形式。灵活的变化,适用场景的多变就非常适合使用这种模式来实现。桥接模式最重要的是找到代码中不同的变化纬度。

状态模式

简介

状态模式(State)允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类。其实就是用一个对象或者数组记录一组状态,每个状态对应一个实现,实现的时候根据状态挨个去运行实现。

优点:

  1. 一个状态对应一个行为,直观清晰,增改方便。

  2. 状态与状态间,行为与行为间彼此独立互不干扰。

  3. 避免对象条件判断语句过多。

  4. 不用执行不必要的判断语句。

缺点:

  1. 需要将事物的不同状态以及对应的行为拆分出来,有时候会过度设计。

  2. 必然会增加事物类和动作类的个数,动作类再根据单一原则,分别拆成几个类,会反而使得代码混乱。

比如下面我们定义一个英雄的状态,名字叫亚索,其中亚索可能同时有好几个状态比如 边走边攻击 —— 我们俗称的“走A”,还有可能释放技能之后接一个“B键回家”的操作,当然最有可能的是eqw闪r行云流水的操作收获一个人头,再接一个ctrl+f6等。

JavaScript设计模式之英雄联盟

如果对这些操作一个个进行处理判断,需要多个if-elseswitch不仅丑陋不说,而且在遇到有组合动作的时候,实现就会更为冗余。那么我们这里的复杂操作,可以使用 状态模式 来实现。

状态模式 的思路是:首先创建一个状态对象或者数组,在对象内部存储需要操作的状态数组或对象,然后状态对象提供一些接口,可以更改状态以及执行动作。

那现在有一个英雄叫做亚索!下面代码,我们就用亚索的状态来实现一下传说中的状态模式:

function YasuoState() {  
    //存储当前即将执行动作的状态!  
    this.currentstate = [];  

    this.Actions = {  
        walk : function(){  
            console.log('walk');  
        },  
        attack : function(){  
            console.log('attack');  
        },  
        magic : function(){  
            console.log('magic');  
        },  
        backhome : function(){  
            console.log('backhome');  
        }  
    };  
}  

YasuoState.prototype.changeState = function() {  
    //清空当前的动作  
    this.currentstate = [];  
    Object.keys(arguments).forEach((i) => this.currentstate.push(arguments[i]))  
    return this;  
}  
YasuoState.prototype.YasuoActions = function() {  
    //当前动作集合中的动作依次执行  
    this.currentstate.forEach((k) => this.Actions[k] && this.Actions[k]())  
    return this;  
}  



var yasuoState = new YasuoState();  

yasuoState.changeState('walk','attack').YasuoActions().changeState('walk').YasuoActions().YasuoActions();  

上面代码成功实现了亚索的状态模式,我们假设他有走路、攻击、释放技能、回家几个状态,其中这几个状态其实是可以同时输入指令的,要不然那些职业选手的高光操作就会在 技能衔接 而出现的卡顿 香消玉殒。

状态模式最常见的就是日常的例子 —— 红绿灯,每当切换状态的时候,执行一次动作。

至于英雄联盟中,最常见的就是边走边攻击,在输入命令后,首先改变了我们对象的状态yasuoState.changeState('magic','backhome'),然后因为在代码中有return this;,可以链式调用接下来的行为,于是我们让它依次执行刚才输入的状态。接下来又一次改变了状态changeState('walk'),并且进行执行。可以看到执行了两次,由于状态并没有再次改变,因此只需要重复执行就可以保证我们的英雄一直往前走下去了。

希望状态模式可以帮助你解决绝大多数,需要切换状态的操作。遇到类似的问题时,可以迅速拿出成熟可靠的状态模式解决之。

总结

本次分享了六种设计模式,分别为构造函数模式,外观模式,代理模式,策略模式 ,桥接模式,状态模式;都是日常开发很常用的设计模式。

其中es6proxy可以访问阮老师博客查看详细api,具体使用也可以借鉴vue源码。代理模式虽然很好用,但也不是任何时候都建议使用,如果你的代码需要获得某些对象的权限,不妨可以使用一下代理模式。在比较简单的场景,可能就没有必要了。

外观模式是最常用的,毕竟每一个js文件总是需要一个入口的。无论是main函数还是init函数,都是起到一个外观包装的作用。当然外观模式并不是必须作为一个文件入口存在,只要能把重复的代码提炼出来,就是一个合理的外观模式。

构造函数模式就不多说了,简单好用。

  • 复制代码是危险的。如果有两段相同的代码,几乎可以说一定是有问题的,因为每次改动,要维护两段代码

  • 尽量减少IO操作,如操作数据库,网络发送,甚至printf ,这些操作比直接操作内存,慢很多倍、

  • 修改Bug时,一定要从最简单的基本的地方开始检查,不要检查到最底层没问题,发现是传入的某个参数是错的。先不要怀疑系统的部分。

  • 设计架构,同时了解细节,有些Bug,调起来可能费时费力,甚至花个二三天,其实当时写的时候,只要稍微注意,就可以轻松避免。避免Bug的代价与找出并修改Bug的代价,实在是差太多了。

  • 把一段长代码,分成很多小函数,便于维护,连自己都不愿看,不愿改的代码,百分百有问题。

  • 写程序时,先把流程搞清楚。把各个流程用的函数写清楚,函数可以留空,这样编程就变成了填空题。

  • 做新功能时,把数据结构的设计,放在较重要的位置

设计模式主要可以帮助我们解决,开发中对代码的设计问题,那我们如何找到合适的对象,并应用合适的设计模式呢?

借用书中的几个提示吧:

  • 寻找合适的对象

  • 决定对象的粒度

  • 决定好这个对象设计的接口

  • 把对象需要的具体函数实现

  • 合理的运用代码复用机制

  • 设计的代码应该可以支持变化,要对变化有预见性

大概是这几种,在 javascript 中涉及编译的场景较少,就不叙述了。

设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

根据上面的几条规则,在开发接口和函数的时候,时刻注意,就可以避免大多数代码设计上的问题,对于以后的维护也会有巨大的帮助。下一个接受代码的人,也会十分感激你的,读代码其实和读书一样,你现在偷懒写的代码可能无所谓,后面接手的人会疯狂吐槽。相反如果你优雅的实现,像我,就会心里由衷的佩服,看到整齐的函数,注释明朗的功能,不得不说,高手确实是高手啊,短短 200 行,让人跪服,就突出一个词 —— 优雅。

收藏
评论区

相关推荐

安利几个JS开发小技巧
开发小技巧 <br/ 1、 转换布尔值 除了常规的布尔值true和false之外,JavaScript还将所有其他值视为 truthy 或falsy。 除非另有定义,否则 JavaScript 中的所有值都是truthy,除了 0,“”,null,undefined,NaN,当然还有false,这些都是falsy 我们可以通过使用负算运算符轻松地
What the f*ck JavaScript?
What the fck JavaScript? 一个有趣和棘手的 JavaScript 示例列表。 JavaScript 是一种很好的语言。
《前端实战总结》之使用解释器模式实现获取元素Xpath路径的算法
前端领域里基于javascript的设计模式和算法有很多,在很多复杂应用中也扮演着很重要的角色,接下来就介绍一下javascript设计模式中的解释器模式,并用它来实现一个获取元素Xpath路径的算法。 上期回顾 《前端实战总结》之迭代器模式的N1种应用场景(https://juejin.im/post/6844904008616771591)
JavaScript进阶之new的实现
我的前端学习笔记📒 最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读 我的前端学习笔记📒(https://www.yuque.com/wanggangfeng
JavaScript进阶之实现拖拽(上)
我的前端学习笔记📒 最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读 我的前端学习笔记📒(https://www.yuque.com/wanggangfeng
对 JavaScript 中事件循环的理解​
一、是什么 JavaScript 在设计之初便是单线程,即指程序运行时,只有一个线程存在,同一时间只能做一件事 为什么要这么设计,跟JavaScript的应用场景有关 JavaScript 初期作为一门浏览器脚本语言,通常用于操作 DOM ,如果是多线程,一个线程进行了删除 DOM ,另一个添加 DOM,此时浏览器该如何处理? 为了解决单
JavaScript 代码整洁之道
作者:alivebao(https://github.com/alivebao/cleancodejs)
js-Answers一
JavaScript的组成 JavaScript 由以下三部分组成: 1. ECMAScript(核心):JavaScript 语言基础 2. DOM(文档对象模型):规定了访问HTML和XML的接口 3. BOM(浏览器对象模型):提供了浏览器窗口之间进行交互的对象和方法 JS的基本数据类型和引用数据类型
JavaScript 是什么?
前言 引用《JavaScript 高级程序设计第四版》中说的话 ——“从简单的输入验证脚本到强大的编程语言,JavaScript 的崛起没有任何人预测到。它很简单,学会用只要几分钟;它又很复杂,掌握它要很多年。要真正学好用好 JavaScript,理解其本质、历史及局限性是非常重要的”。 面试官:JavaScript 是什么? 我:
理解 Javascript 中的 Async / Await
在本文中,我们将探讨async/await,对于每个Javascript开发人员来说,是异步编程的首选工具。如果您不熟悉javascript,请不要担心,本文将帮助您async/await从头开始理解。 介绍async/await 是javascript中的一种模式,可使您的代码以同步方式执行,但又不影响javascript的异步行为。 定义异步功能要定义一
JavaScript 和 Node.js 中事件循环
1.JavaScript中事件循环可以参考《JavaScript忍者秘籍第二版》第十三章,讲解的很好。JavaScript中事件循环,主要就在理解宏任务和微任务这两种异步任务。宏任务(macrotask): setTimeOut 、 setInterval 、 setImmediate 、 I/O 、 各种callback、 UI渲染 、messageCh
了解什么是 TypeScript
内容纲要 了解什么是 TypeScript TypeScript 基本语法 TypeScript 介绍 TypeScript 是什么TypeScript 是 JavaScript 的强类型版本。然后在编译期去掉类型和特有语法,生成纯粹的 JavaScript代码。由于最终在浏览器中运行的仍然是 JavaScript,所以 TypeScript 并
一篇文章带你了解JavaScript作用域
在JavaScript中,对象和函数也是变量。在JavaScript中,作用域是你可以访问的变量、对象和函数的集合。JavaScript 有函数作用域: 这个作用域在函数内变化。一、本地JavaScript变量一个变量声明在JavaScript函数内部,成为函数的局部变量。局部变量有局部作用域: 它们只能在函数中访问。JS://code here can n
javascript实践教程-01-javascript介绍
本节目标1. 了解javascript是什么。2. 了解javascript能干什么。 内容摘要本篇介绍了javascript是什么,为什么要用javascript,ECMAScript标准是什么等。阅读时间大约510分钟。 javascript是什么?javascript是世界上最流行的脚本语言,因为你在电脑、手机、平板上浏览的所有的网页,以及无数基于HT
javascript实践教程-02-javascript入门
本节目标1. 掌握如何编写javascript代码。2. 掌握javascript的3个弹框。3. 掌握javascript的注释。4. 掌握浏览器的调试工具控制台。 内容摘要本篇介绍了如何在网页上编写js代码,如何引入外部js代码文件,js的3个弹框、注释语法,还有浏览器调试工具的控制台使用。阅读时间1520分钟。 script标签如果我们需要在网页中编写