{"id":18739021,"url":"https://github.com/gaubee/jscope","last_synced_at":"2025-11-18T12:30:15.254Z","repository":{"id":149112359,"uuid":"56037542","full_name":"Gaubee/jScope","owner":"Gaubee","description":"模拟JavaScript作用域的数据缓存库。可以用于JS-VM中实现Javascript数据对象的存储。","archived":false,"fork":false,"pushed_at":"2016-04-23T06:53:51.000Z","size":39,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-28T17:44:43.488Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Gaubee.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-12T06:34:40.000Z","updated_at":"2022-02-28T13:28:22.000Z","dependencies_parsed_at":"2023-04-26T01:01:55.567Z","dependency_job_id":null,"html_url":"https://github.com/Gaubee/jScope","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gaubee%2FjScope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gaubee%2FjScope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gaubee%2FjScope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gaubee%2FjScope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gaubee","download_url":"https://codeload.github.com/Gaubee/jScope/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239616501,"owners_count":19669061,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T15:32:26.966Z","updated_at":"2025-11-18T12:30:15.191Z","avatar_url":"https://github.com/Gaubee.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jScope\r\n\r\n模拟JavaScript作用域的数据缓存库。可以用于JS-VM中实现Javascript数据对象的存储。\r\nJavaScript 有两个范围：全局和局部，还有let、const关键字所带来的块作用域的支持。\r\n\r\n# API\r\n\r\n## 作用域的使用方式\r\n\r\n```js\r\nvar sp = new jScope(this/* window or global*/,{\r\n    /*\r\n     * e.target 指向报错的作用域\r\n     * e.target.name 作用域名\r\n     */\r\n    onerror:function(e){\r\n        console.log(e.message);\r\n        // throw e 如果在这里抛出异常，会直接终止执行中的语句。\r\n    }\r\n});\r\n\r\n// 开启一级新的作用域，给予一个名字，可空\r\nsp.push(\"test\")\r\n\r\nsp.set(\"b\", 2);// 因为没有提前声明b变量，所以b被定义到全局作用域中\r\n// 定义局部变量\r\nsp.Var(\"a\", 1);// 定义并初始化a变量\r\nsp.set(\"a\", 3);// 赋值a变量\r\n\r\nconsole.log(sp.get(\"a\") + sp.get(\"b\"))// 5： 取得局部a变量与全局b变量，执行valueOf\r\n\r\n// 开启块作用域\r\nsp.pushBlock(\"zz\");\r\nsp.Var(\"a\", 4);\r\nsp.Let(\"c\", 5);\r\nsp.Const(\"d\", 6);\r\n\r\nconsole.log(sp.get(\"c\").valueOf()); // c\r\nsp.Const(\"d\", 6);\r\n// 关闭快作用域\r\nsp.popBlock();\r\n\r\n//var声明的对象是没有块作用域概念的，所以Var a等同于Set a\r\nconsole.log(sp.get(\"a\").valueOf()); // 4\r\nconsole.log(sp.get(\"c\").valueOf()); // 退出了快作用域，没有声明c变量，触发onerror\r\n// 关闭局部变量，返回数据，否则会被清空\r\nsp.pop(sp.get(\"a\"));\r\n```\r\n\u003e注意的是，使用Var的时候，需要使用者手动执行“变量提升”，就是将一个局部作用域里面需要用到的Var声明的对象前置声明\r\n\r\n## JS对象的属性的相关操作\r\n\r\n```js\r\nvar source_obj = {a:\"a\"}\r\nvar obj = sp.Var(\"obj\", source_obj); // 传入一个参考对象，这个参考对象不会发生任何改变\r\nconsole.log(obj === sp.get(\"obj\")); // 都是这个obj变量的“引用”\r\nconsole.log(obj.valueOf()); // 参考对象被按需浅复制(1) {a: \"a\"} !== source_obj\r\n\r\nvar obj_a = obj.dot(\"a\"); // 取属性\r\nconsole.log(obj_a.valueOf(\"\")); //属性取值\r\n\r\nobj_a.set(1)//重新赋值 等同于obj.set(\"a\", 1)\r\n\r\nobj2 = sp.Var(\"obj2\", source_obj);//参考对象仅仅用于参考，而不作为真正的操作对象。所以同个参考对象所创建出来的变量时不一样的\r\nconsole.log(obj2.equal(obj));// false: obj2.valueOf() === obj.valueOf() \r\n\r\nvar obj3 = sp.Var(\"obj3\");\r\nconsole.log(obj3.like(null));// true： obj3.valueOf() /*undefined*/ == null \r\n```\r\n\r\n(1). 按需浅复制： 在参考变量传入的时候，会被先浅复制一层。而当在执行深处数据操作的时候，jScope是以不改变参考对象的原则来进行操作。所以jScope会一层层地执行浅复制到指定层。因为是按层次需要，所以也不会造成死循环。\r\n\u003e PS:如果使用深复制，会造成性能问题，特别是初始化的时候使用global对象来进行深复制。所以按需浅复制是一个处于浅复制与深复制的折衷方案。\r\n\u003e 注意：因为Function对象自带着作用域对象，所以如果有属性的操作Function对象么，那么是会反应到参考对象中的。\r\n\r\n## 关于内存管理\r\n\r\n框架会在作用域关闭的时候(`.pop(returner)`)释放内存，所以要注意的是闭包的使用。\r\n因为全权交托给使用者操作，所以闭包内的变量也需要手动声明缓存（使用者务必需要有对JS作用域明确的了解）：\r\n\r\n_错误的示例_\r\n```js\r\nvar csp = sp.push();\r\ncsp.Var(\"a\"); // 同sp.Var(\"a\")，但是fun被pop所return后是可以执行在pop之外的，所以到时候sp.get(\"a\")是访问不到这个对象的\r\nvar fun = sp.Var(\"fun\",function(){\r\n    console.log(csp.get(\"a\")); // ! Error\r\n});\r\nvar returner = sp.pop(fun);\r\n\r\n//...\r\nreturner.run();// throw no defined\r\n```\r\n\u003e 上面代码中要注意的问题有：sp返回了一个child-sp(csp)，而csp在一个fun函数中调用，这样会引起闭包导致csp这个作用域对象无法被回收，即便a是被释放了。但是根据需求来说，我们要的不是这个作用域对象，而是a对象\r\n\r\n_正确写法_\r\n```js\r\nsp.push()\r\nvar a = sp.Var(\"a\",1);// 将a变量的引用取出来，才能在闭包中使用\r\nvar fun = sp.Var(\"fun\",function(){\r\n    console.log(a.valueOf()); // 1\r\n});\r\nsp.pop(fun);//释放作用域对象csp\r\n\r\n//...\r\nreturner.run();// ok\r\n```\r\n\u003e这里在csp被释放后，由于a对象被fun闭包所引用，所以是不会被系统内存回收的。\r\n\r\n## 块级作用域（插件）（时间关系暂未实现）\r\n\r\nES6中块级作用域有一个特性：“暂时性死区（TDZ）”。\r\n由于jScope无法预见AST，所以是无法检测TDZ的（特别是typeof、delete关键字会引发错误，所以如果要完全模拟，就需要求使用者前置声明这个块作用域中有哪些变量将被定义）。比如下面代码：\r\n```js\r\n/* 源码意图\r\n{\r\n    a = 1;\r\n    let a = 2\r\n}\r\n*/\r\nsp.set(\"a\",1)\r\nsp.Let(\"a\",2)\r\n/* 实际效果\r\na = 1\r\n{\r\n    let a = 2\r\n}\r\n*/\r\n\r\n// typeof、delete 会引发异常\r\ntypeof a\r\nlet a = 0\r\n// 不会引发异常\r\ntypeof b\r\ndelete b\r\nlet a = 0\r\n```\r\n\r\n需要理解的是jScope的实现方式：块级作用域的使用就意味着在使用的时候会增加判断量，需要判断变量是否在块级作用域中，是否重复声明等等。\r\n所以jScope做了按需加载：使用者通过第一次使用Let、Const、pushBlock、Lock的时候，或者手动在声明使用严格模式，会初始化块级作用域功能。\r\n\r\n这里要说的是pushBlock这个接口。如果要启用TDZ，那么就要手动使用Lock接口将要使用到的块级作用域变量前置声明：\r\n```js\r\n// 情景0\r\nvar sp = new Scope(global,{\r\n    strict: true\r\n}) \r\n// 等效运行了：\r\nvar sp = new Scope(global); sp.pushBlock();\r\n// 不同的是：strict会参数会导致子作用域继承这个特性\r\n\r\n// 情景1\r\nsp.Lock(\"a\",\"b\");// 有a、b两个参数将作为块级作用域的值\r\nsp.TypeOf(\"a\");// ReferenceError，TDZ特性引发错误\r\nsp.Del(\"a\");// ReferenceError，TDZ特性引发错误\r\n\r\n// 情景2\r\nsp.TypeOf(\"a\");// undefined ，以为a是普通的变量\r\nsp.Let(\"a\", 1)\r\n\r\nsp.destroy();// 自动销毁子集作用域以及块级作用域。等同于调用了sp.popBlock(),sp.destory()\r\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaubee%2Fjscope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgaubee%2Fjscope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaubee%2Fjscope/lists"}