九个实用的JavaScript开发技巧与实现

作者:代码拾荒

时间:2026-10-26

浏览:545

本文整理了九个实用的JavaScript开发技巧,涵盖动态加载脚本、简易模板引擎实现、利用reduce转换数据结构、函数默认值处理、单次执行封装、柯里化应用、单例模式、CommonJS规范模拟及递归获取属性。通过具体代码示例,帮助开发者提升代码质量与执行效率。

文章目录

1、动态加载 JS 文件

在特定开发场景中,尤其是库或框架构建时,常需动态加载并执行 JavaScript 文件。以下代码利用 Promise 对这一过程进行了封装。

function loadJS(files, done) {
  // 获取head标签
  const head = document.getElementsByTagName('head')[0];
  Promise.all(files.map(file => {
    return new Promise(resolve => {
      // 创建script标签并添加到head
      const s = document.createElement('script');
      s.type = "text/javascript";
      s.async = true;
      s.src = file;
      // 监听load事件,如果加载完成则resolve
      s.addEventListener('load', (e) => resolve(), false);
      head.appendChild(s);
    });
  })).then(done);  // 所有均完成,执行用户的回调事件
}

loadJS(["test1.js", "test2.js"], () => {
  // 用户的回调逻辑
});

该实现的核心在于两点:一是使用 Promise 处理异步逻辑,二是通过创建 script 标签来加载并执行 JS 代码。

2、实现模板引擎

以下示例用极简代码实现了动态模板渲染引擎,不仅支持普通变量替换,还涵盖 for 循环、if 判断等动态 JS 语法逻辑。具体实现细节详见文章《面试官问:你能手写一个模版引擎吗?》,感兴趣者可自行查阅。

// 这是包含了js代码的动态模板
var template =
'My avorite sports:' +
'<%if(this.showSports) {%>' +
    '<% for(var index in this.sports) {   %>' +
    '<a><%this.sports[index]%></a>' +
    '<%}%>' +
'<%} else {%>' +
    '<p>none</p>' +
'<%}%>';
// 这是我们要拼接的函数字符串
const code = `with(obj) {
  var r=[];
  r.push("My avorite sports:");
  if(this.showSports) {
    for(var index in this.sports) {
      r.push("<a>");
      r.push(this.sports[index]);
      r.push("</a>");
    }
  } else {
    r.push("<span>none</span>");
  }
  return r.join("");
}`
// 动态渲染的数据
const options = {
  sports: ["swimming", "basketball", "football"],
  showSports: true
}
// 构建可行的函数并传入参数,改变函数执行时this的指向
result = new Function("obj", code).apply(options, [options]);
console.log(result);

3、利用 reduce 进行数据结构的转换

前端开发中,常需将后端返回的数据转换为符合业务逻辑的格式,或将组件数据转换后传回后端。此时,reduce 是一个极为强大的工具。

const arr = [
    { classId: "1", name: "张三", age: 16 },
    { classId: "1", name: "李四", age: 15 },
    { classId: "2", name: "王五", age: 16 },
    { classId: "3", name: "赵六", age: 15 },
    { classId: "2", name: "孔七", age: 16 }
];

groupArrayByKey(arr, "classId");

function groupArrayByKey(arr = [], key) {
    return arr.reduce((t, v) => (!t[v[key]] && (t[v[key]] = []), t[v[key]].push(v), t), {})
}

许多复杂逻辑若使用 reduce 处理,往往能保持代码简洁。

4、添加默认值

当方法需要用户传入参数时,通常有两种处理方式:若用户未传参,则提供默认值;或者强制要求传参,否则直接抛出错误。

function double() {
    return value *2
}

// 不传的话给一个默认值0
function double(value = 0) {
    return value * 2
}

// 用户必须要传一个参数,不传参数就抛出一个错误

const required = () => {
    throw new Error("This function requires one parameter.")
}
function double(value = required()) {
    return value * 2
}

double(3) // 6
double() // throw Error

listen 方法用于创建 NodeJS 原生 http 服务并监听端口。在服务回调中创建 context,随后调用用户注册的回调函数并传递生成的 context。接下来查看 createContext 和 handleRequest 的具体实现。

5、函数只执行一次

某些场景下,特定函数或绑定的方法仅允许执行一次。

export function once (fn) {
  // 利用闭包判断函数是否执行过
  let called = false
  return function () {
    if (!called) {
      called = true
      fn.apply(this, arguments)
    }
  }
}

6、实现 Curring

JavaScript 的柯里化是将接收多个参数的函数转换为一系列仅接收一个参数的函数的过程。这能更灵活地使用函数,减少重复代码,并提升代码可读性。

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      };
    }
  };
}

function add(x, y) {
  return x + y;
}

const curriedAdd = curry(add);

console.log(curriedAdd(1)(2)); // 输出 3
console.log(curriedAdd(1, 2)); // 输出 3

通过柯里化,可将常见功能模块化,如验证、缓存等。这有助于提高代码的可维护性和可读性,降低出错概率。

7、实现单例模式

JavaScript 的单例模式是一种常用设计模式,确保类只有一个实例并提供全局访问点。在 JS 中应用广泛,如购物车、缓存对象、全局状态管理等场景。

let cache;
class A {
  // ...
}

function getInstance() {
  if (cache) return cache;
  return cache = new A();
}

const x = getInstance();
const y = getInstance();

console.log(x === y); // true

8、实现 CommonJs 规范

CommonJS 规范的核心思想是将每个文件视为一个模块,拥有独立作用域,其中的变量、函数和对象均为私有,外部无法直接访问。访问模块数据必须通过导出(exports)和导入(require)机制。

// id:完整的文件名
const path = require('path');
const fs = require('fs');
function Module(id){
    // 用来唯一标识模块
    this.id = id;
    // 用来导出模块的属性和方法
    this.exports = {};
}

function myRequire(filePath) {
    // 直接调用Module的静态方法进行文件的加载
    return Module._load(filePath);
}

Module._cache = {};
Module._load = function(filePath) {
    // 首先通过用户传入的filePath寻址文件的绝对路径
    // 因为再CommnJS中,模块的唯一标识是文件的绝对路径
    const realPath = Module._resoleveFilename(filePath);
    // 缓存优先,如果缓存中存在即直接返回模块的exports属性
    let cacheModule = Module._cache[realPath];
    if(cacheModule) return cacheModule.exports;
    // 如果第一次加载,需要new一个模块,参数是文件的绝对路径
    let module = new Module(realPath);
    // 调用模块的load方法去编译模块
    module.load(realPath);
    return module.exports;
}

// node文件暂不讨论
Module._extensions = {
   // 对js文件处理
  ".js": handleJS,
  // 对json文件处理
  ".json": handleJSON
}

function handleJSON(module) {
 // 如果是json文件,直接用fs.readFileSync进行读取,
 // 然后用JSON.parse进行转化,直接返回即可
  const json = fs.readFileSync(module.id, 'utf-8')
  module.exports = JSON.parse(json)
}

function handleJS(module) {
  const js = fs.readFileSync(module.id, 'utf-8')
  let fn = new Function('exports', 'myRequire', 'module', '__filename', '__dirname', js)
  let exports = module.exports;
  // 组装后的函数直接执行即可
  fn.call(exports, exports, myRequire, module,module.id,path.dirname(module.id))
}

Module._resolveFilename = function (filePath) {
  // 拼接绝对路径,然后去查找,存在即返回
  let absPath = path.resolve(__dirname, filePath);
  let exists = fs.existsSync(absPath);
  if (exists) return absPath;
  // 如果不存在,依次拼接.js,.json,.node进行尝试
  let keys = Object.keys(Module._extensions);
  for (let i = 0; i < keys.length; i++) {
    let currentPath = absPath + keys[i];
    if (fs.existsSync(currentPath)) return currentPath;
  }
};

Module.prototype.load = function(realPath) {
  // 获取文件扩展名,交由相对应的方法进行处理
  let extname = path.extname(realPath)
  Module._extensions[extname](this)
}

上述代码对 CommonJS 规范进行了简单实现,核心解决了作用域隔离问题,并提供了 Myrequire 方法用于加载方法和属性。关于此实现的详细说明,可参考文章《38 行代码带你实现 CommonJS 规范》,感兴趣者可自行阅读。

9、递归获取对象属性

若让我选择一个最广泛使用的设计模式,我会选观察者模式;若选择遇到的最多算法思维,那无疑是递归。递归通过将原问题分解为结构相同的子问题,依次解决子问题并组合结果,最终获得原问题的答案。

const user = {
  info: {
    name: "张三",
    address: { home: "Shaanxi", company: "Xian" },
  },
};

// obj是获取属性的对象,path是路径,fallback是默认值
function get(obj, path, fallback) {
  const parts = path.split(".");
  const key = parts.shift();
  if (typeof obj[key] !== "undefined") {
    return parts.length > 0 ?
      get(obj[key], parts.join("."), fallback) :
      obj[key];
  }
  // 如果没有找到key返回fallback
  return fallback;
}

console.log(get(user, "info.name")); // 张三
console.log(get(user, "info.address.home")); // Shaanxi
console.log(get(user, "info.address.company")); // Xian
console.log(get(user, "info.address.abc", "fallback")); // fallback

以上挑选了 9 个我认为较为实用的 JS 技巧,希望能对大家有所帮助。

到此这篇关于九个超级好用的Javascript 技巧的文章就介绍到这了,更多相关Javascript 技巧内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章