|
3 | 3 | 函数是JS中常用的功能;本质是十分简单的,实现高内聚,低耦合;因为用在不同的场景有不同的功能;一些额外的功能可以通过使用闭包来实现。此外,由于所有的函数都是对象,所以使用函数指针非常简单。这些令 JavaScript 函数不仅有趣而且强大。以下几节描绘了几种在 JavaScript 中使用函数的高级方法。 |
4 | 4 |
|
5 | 5 | - 数据类型的安全检测 |
6 | | -- 作用域安全的构造函数 |
| 6 | +- 构造函数的安全作用域 |
7 | 7 | - 惰性载入函数 |
8 | 8 | - 函数绑定 |
9 | 9 | - 函数柯里化 |
|
51 | 51 |
|
52 | 52 | 在 Web 开发中能够区分原生与非原生 JavaScript 对象非常重要。只有这样才能确切知道某个对象到底有哪些功能。这个技巧可以对任何对象给出正确的结论。 |
53 | 53 |
|
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 属性。 |
0 commit comments