65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Web 应用程序中使用 Vanilla JS

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.45/5 (3投票s)

2018 年 8 月 10 日

CPOL

5分钟阅读

viewsIcon

5627

为 IE 和其他过时浏览器编写 JavaScript 意味着 ES6 中的类和其他有用的功能将不可用。但是,可以实现类似的效果,而且实际上很容易做到!

为什么使用 原生 JS 而不是众多可用的框架,甚至 TypeScript?如果选择已经做出,那么答案基本上无关紧要。然而,决定用其他替代品替换所有用例的 JavaScript 是一种脱离实际的绝对主义。本文将描述原生 JS 的使用,让您自己选择使用哪种语言。

类定义和命名空间

ES6 类尚未在浏览器中完全支持。本文提到的大部分限制在开发 ES5 时最为相关——例如,为 Internet Explorer 或其他过时浏览器开发。即使没有类的完全支持,在 JavaScript 中也可以实现类似的效果,而且实际上很容易做到!

我们首先要确保类定义是受限的。这意味着它不应该用方法和变量污染全局命名空间。这可以通过使用闭包来实现——一个特殊的闭包,称为立即执行函数表达式(IIFE)

(function (global) {
  "use strict";

  global.API = new MyObject();

  function MyObject() {
    var self = this;

    ... var privateVariable ...

    ... function privateMethod() ...

    ... self.publicMethod ...
  }
})((1,eval)('this'));

请注意,全局命名空间已传递给 IIFE——因为它们只是方法,所以可以像这样使用它们!如果您想了解有关如何获取全局命名空间的更多信息,请查看这个发人深省的 StackOverflow 帖子:(1,eval)(‘this’) vs eval(‘this’) in JavaScript?

“严格模式”

"use strict"; //<a href="https://mdn.org.cn/en-US/docs/Web/JavaScript/Reference/Strict_mode">
              //seriously, do it.</a>

类可以初始化并存储在全局范围内,例如在单个应用程序特定的命名空间内

(function (global,app,http) {
  "use strict";
  global[app] = global[app] || {};
  global[app][http] = new Http();
  // global.App.http.publicMethods()

  function Http() {
     var self = this;
     // var privateVariables ...

     // self.publicMethods = function ...

     // function privateFunctions() ...
  }
})((1,eval)('this'),'App','http');

我认为将客户端 JavaScript 编写成 API 更容易。利用这种设计模式可以提高代码质量和可维护性。在上面的代码中,Http 的实例被分配给全局.App 命名空间中的http 属性。当然,这里应该包含我们用于发出 HTTP 请求的方法!通过这种方式处理应用程序的客户端 JavaScript,代码组织是最好的方面之一。通常,存储的将是构造函数本身,而不是实例——这允许应用某些SOLID 原则

构造函数

Http 函数是一种特殊的函数——一个构造函数。这意味着可以使用构造函数调用中的new 运算符创建实例。

function MyObject() {
}

var instance = new MyObject();

如果您以前创建过面向对象代码的实例,这看起来应该很熟悉。

捕获 this

this 并不总是相同的事实既是 JavaScript 的力量,也是其诅咒Http 构造函数的第一行捕获特定上下文中的this,以帮助克服诅咒并利用其力量。

function Http() {
  var self = this;

  ...
}

在构造函数的作用域中,this 指的是 Http 对象。声明一个私有变量并对其进行初始化,以捕获它并使其可供 Http 的所有公共私有成员使用——无论在调用这些成员时this 是什么。只捕获一次 this 并在相应构造函数的作用域中捕获,可以减少 this 兑现其诅咒的可能性!

私有成员

Http 构造函数作用域中创建的变量和函数将可供 Http 对象内的所有公共私有成员使用。

function Http() {
  var self = this,
      eventHandlers = {};

  function addEventHandler(event, handler) { }
  function removeEventHandler(event, handler) { }
}

在这种情况下,selfeventHandlers 以及添加/删除事件处理函数的函数是 Http私有成员。它们不对外部源开放——只有 Http公共私有成员才能访问 Http私有成员。

公共成员

暴露在 Http 对象上的属性和方法,例如原型链中存在的属性和方法,可以从外部源访问,被认为是公共的。

function Http() {
  var self = this;

  self.get = function (request) { ...
  self.post = function (request, data) { ...
}

在构造函数中将公共成员添加到 self 变量。这允许其他代码执行 Http 实例的操作。

静态成员

成员也可以是静态的。通过在构造函数本身上声明一个变量,可以为其分配一个值、实例或函数,该值、实例或函数是公共的,同时不依赖于使用构造函数创建实例。

function Http() {
}

Http.setup = function () { ...

静态 Http 成员可以在不创建 Http 实例的情况下使用。

// ... application code doesn't create an Http instance
Http.setup();
// ... application code doesn't create an Http instance

该成员是公共的,并且在 Http 构造函数可用的任何地方都可以使用。

执行上下文

在不深入探讨JavaScript 中的执行上下文的情况下,有几点需要注意。本节将描述几种不同的执行上下文以及 JavaScript 代码执行的集成点。

全局上下文

只有一个全局上下文——或全局作用域或全局命名空间。在函数外部定义的任何变量都存在于全局上下文中。

var x = 9;
function XManager() {
  var self = this;
  self.getX = function () { return x; }
  self.setX = function (value) { x = value; }
}

全局作用域变量 xXManager 函数外部定义,并被赋予值 9。当调用 getX 时,它将返回全局作用域的 x(值为 9)。

局部作用域——函数执行上下文

全局作用域的替代是局部作用域。局部作用域由函数执行上下文定义。

var x = 9;
function XManager() {
  var self = this,
      x = 10;
  ...
  self.getInstanceX = function () {
    return x; // returns 10
  }
}

在这种情况下,变量 x 被声明了两次。第一次是在全局执行上下文中。此变量在 XManager 中可访问。在 XManager 构造函数中,声明了私有变量 x 并将其初始化为 10getInstanceX 方法将返回其执行上下文堆栈中最先出现的变量 x

ecstack

执行上下文堆栈(David Shariff)

getInstanceX 方法是“当前活动”,接下来是 XManager私有变量 x,然后是全局作用域变量 x,最后是全局执行上下文。所有这些都是为了解释为什么 getInstanceX 返回 10 而不是 9。非常强大!

let & 块级作用域

在讨论执行上下文时,我不能不提到关键字 let。此关键字允许声明块级作用域变量。与 ES6 类一样,如果需要支持过时的浏览器,则 let 关键字将不可用。

function Start() {
  let x = 9;          // variable assigned to value 9
  function XManager() {
    let x = 10;       // different variable assigned to value 10
    function getX() {
      console.log(x); // return 10
    }
    console.log(x);   // return 10
    getX();
  }
  console.log(x);     // return 9
  XManager();
}
Start();

块作用域变量在其上下文(Start)及其包含的子块(XManager)内可访问。与 var 的主要区别在于,var 的作用域是整个封闭函数。这意味着,在使用 let 时,XManager 和包含的子块(getX)可以访问被赋值为 10 的新变量 x,而 Start 上下文中的变量 x 仍将具有值 9

事件处理程序

用户通过 DOM 事件与渲染的 HTML 交互时,会触发客户端 JavaScript 代码。当触发事件时,其订阅者(事件处理程序)将被调用来处理该事件。

HTML – 事件订阅

<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
<div id="submit">Button</div>

JAVASCRIPT – 事件订阅

var button = document.getElementById("submit");
button.addEventHandler('click', clickHandler);

JAVASCRIPT – 事件处理程序

function clickHandler() {
  console.log("Click event handled!");
}

事件处理程序是用户与 HTML 的交互与 JavaScript 中的应用程序 API 之间的集成点。

编写客户端 JavaScript 时,理解如何创建对象和执行上下文非常重要。将 JavaScript 设计成 API 将有助于进一步管理该语言的优缺点。

© . All rights reserved.