Skip to content

Commit bc571ad

Browse files
committed
构造函数的安全作用域
1 parent 7244b4f commit bc571ad

3 files changed

Lines changed: 131 additions & 13 deletions

File tree

JS高级技巧/高级函数.md

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
函数是JS中常用的功能;本质是十分简单的,实现高内聚,低耦合;因为用在不同的场景有不同的功能;一些额外的功能可以通过使用闭包来实现。此外,由于所有的函数都是对象,所以使用函数指针非常简单。这些令 JavaScript 函数不仅有趣而且强大。以下几节描绘了几种在 JavaScript 中使用函数的高级方法。
44

55
- 数据类型的安全检测
6-
- 作用域安全的构造函数
6+
- 构造函数的安全作用域
77
- 惰性载入函数
88
- 函数绑定
99
- 函数柯里化
@@ -51,4 +51,94 @@
5151

5252
在 Web 开发中能够区分原生与非原生 JavaScript 对象非常重要。只有这样才能确切知道某个对象到底有哪些功能。这个技巧可以对任何对象给出正确的结论。
5353

54-
**请注意, Object.prototpye.toString() 本身也可能会被修改。上面的技巧假设 Object.prototpye.toString() 是未被修改过的原生版本。**
54+
**请注意, Object.prototpye.toString() 本身也可能会被修改。上面的技巧假设 Object.prototpye.toString() 是未被修改过的原生版本。**
55+
56+
# 构造函数的安全作用域
57+
58+
当构造函数没有使用new生成实例,直接使用的时候;由于构造函数中 this 对象是在运行时绑定的 ; this关键字会映射到全局对象window上;导致错误对象属性的意外增加。
59+
60+
function Person(name,age,job){
61+
this.name=name;
62+
this.age=age;
63+
this.job=job;
64+
}
65+
66+
var person=Person("zhu",26,"WEB");
67+
console.log(person);//undefined;
68+
console.log(window.name);//"zhu;
69+
console.log(window.age);//26;
70+
console.log(window.job);//WEB";
71+
72+
这里,原本针对 Person 实例的三个属性被加到 window 对象上,因为构造函数是作为普通函数调用的,忽略了 new 操作符。这个问题是由 this 对象的晚绑定造成的,在这里 this 被解析成了 window对象。由于 window 的 name 属性是用于识别链接目标和 frame 的,所以这里对该属性的偶然覆盖可能会导致该页面上出现其他错误。这个问题的解决方法就是创建一个作用域安全的构造函数
73+
74+
作用域安全的构造函数在进行任何更改前,首先确认 this 对象是正确类型的实例。如果不是,那么会创建新的实例并返回。
75+
76+
如下:
77+
78+
function Person(name,age,job){
79+
if(this instanceof Person){
80+
console.log("Person用法 - 正确");
81+
this.name=name;
82+
this.age=age;
83+
this.job=job;
84+
}else{
85+
console.log("Person用法 - 不正确");
86+
return new Person(name,age,job);
87+
}
88+
}
89+
var person=Person("hahahah",26,"WEB");
90+
console.log(person);
91+
console.log(person.name);//"hahahah";
92+
console.log(window.name);//""
93+
console.log(typeof window.name);//string
94+
console.log(window.age);//26;
95+
console.log(window.job);//WEB";
96+
97+
这段代码中的 Person 构造函数添加了一个检查并确保 this 对象是 Person 实例的 if 语句,它表示要么使用 new 操作符,要么在现有的 Person 实例环境中调用构造函数。任何一种情况下,对象初始化都能正常进行。如果 this 并非 Person 的实例,那么会再次使用 new 操作符调用构造函数并返回结果。最后的结果是,调用 Person 构造函数时无论是否使用 new 操作符,都会返回一个 Person 的新实例,这就避免了在全局对象上意外设置属性。
98+
99+
** 实现这个模式后,你就锁定了可以调用构造函数的环境。如果你使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏。**
100+
101+
function Parent(name,age,job){
102+
if(this instanceof Parent){
103+
console.log("Chilren用法 - 正确");
104+
this.name=name;
105+
this.age=age;
106+
this.job=job;
107+
}else{
108+
console.log("Chilren用法 - 不正确");
109+
return new Parent(name,age,job);
110+
}
111+
}
112+
function Chilren(parentName){
113+
Parent.call(this,"child","1","null");
114+
this.name=parentName;
115+
}
116+
117+
var target=new Chilren("ooooo");
118+
console.log(target);//{name: "ooooo"}
119+
console.log(target.age);//undefined
120+
121+
在这段代码中, Parent 构造函数是作用域安全的,然而 Chilren 构造函数则不是。新创建一个 Chilren 实例之后,这个实例应该通过 Parent.call() 来继承 Parent 的 age 属性。但是,由于 Parent 构造函数是作用域安全的, this 对象并非 Parent 的实例,所以会创建并返回一个新的 Parent 对象。 Chilren 构造函数中的 this 对象并没有得到增长,同时 Parent.call() 返回的值也没有用到,所以 Chilren 实例中就不会有 age 属性。
122+
123+
function Parent(name,age,job){
124+
if(this instanceof Parent){
125+
console.log("Chilren用法 - 正确");
126+
this.name=name;
127+
this.age=age;
128+
this.job=job;
129+
}else{
130+
console.log("Chilren用法 - 不正确");
131+
return new Parent(name,age,job);
132+
}
133+
}
134+
function Chilren(parentName){
135+
Parent.call(this,"child","1","null");
136+
this.name=parentName;
137+
}
138+
Chilren.prototype = new Parent();//【加上这一行代码,让Chilren的实例可以指到Chilren即可】
139+
var target=new Chilren("ooooo");
140+
console.log(target);//Chilren {name: "ooooo", age: "1", job: "null"}
141+
console.log(target.age);//1
142+
143+
上面这段重写的代码中,一个 Rectangle 实例也同时是一个 Polygon 实例,所以 Polygon.call()
144+
会照原意执行,最终为 Rectangle 实例添加了 sides 属性。

index.js

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1-
var testStr = 'cccccccc';
2-
var testAry = [2,3,4,5];
3-
var testObj = {
4-
name:"zhu",
5-
age:26,
6-
gender:"man"
7-
};
8-
9-
console.log(Object.prototype.toString.call(testStr));//[object String]
10-
console.log({}.toString.call(testAry));//[object Array]
11-
console.log({}.toString.call(testObj));//[object Object]
1+
function Parent(name,age,job){
2+
if(this instanceof Parent){
3+
console.log("Chilren用法 - 正确");
4+
this.name=name;
5+
this.age=age;
6+
this.job=job;
7+
}else{
8+
console.log("Chilren用法 - 不正确");
9+
return new Parent(name,age,job);
10+
}
11+
}
12+
function Chilren(parentName){
13+
Parent.call(this,"child","1","null");
14+
this.name=parentName;
15+
}
16+
Chilren.prototype = new Parent();//【加上这一行代码,让Chilren的实例可以指到Chilren即可】
17+
var target=new Chilren("ooooo");
18+
console.log(target);//Chilren {name: "ooooo", age: "1", job: "null"}
19+
console.log(target.age);//1

test-file.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,25 @@
77
<body>
88
<p><b>hello </b>word!</p>
99
<script src="index.js"></script>
10+
<!--<script>
11+
function Person(name,age,job){
12+
if(this instanceof Person){
13+
console.log("Person用法 - 正确");
14+
this.name=name;
15+
this.age=age;
16+
this.job=job;
17+
}else{
18+
console.log("Person用法 - 不正确");
19+
return new Person(name,age,job);
20+
}
21+
}
22+
var person=Person("hahahah",26,"WEB");
23+
console.log(person);
24+
console.log(person.name);//"hahahah";
25+
console.log(window.name);//""
26+
console.log(typeof window.name);//string
27+
console.log(window.age);//26;
28+
console.log(window.job);//WEB";
29+
</script>-->
1030
</body>
1131
</html>

0 commit comments

Comments
 (0)