JavaScript之原型、函数、实例
<h2>JavaScript 函数语法</h2><p>函数就是包裹在花括号中的代码块,前面使用了关键词 function:</p>
<div class="code">
<div> function <em>functionname</em>()<br>
{<br> <em>// 执行代码</em><br>
}</div>
</div>
<p>当调用该函数时,会执行函数内的代码。</p>
<p>可以在某事件发生时直接调用函数(比如当用户点击按钮时),并且可由 JavaScript 在任何位置进行调用。</p>
<table class="lamp">
<tbody>
<tr><th width="34"><img src="https://www.runoob.com/images/lamp.jpg"></th>
<td>JavaScript 对大小写敏感。关键词 function 必须是小写的,并且必须以与函数名称相同的大小写来调用函数</td>
</tr>
</tbody>
</table>
<p>在调用函数时,您可以向其传递值,这些值被称为参数。</p>
<p>这些参数可以在函数中使用。</p>
<p>您可以发送任意多的参数,由逗号 (,) 分隔:</p>
<div class="code">
<div> myFunction(<em>argument1,argument2</em>)<br>
</div>
</div>
<p>当您声明函数时,请把参数作为变量来声明: </p>
<div class="code">
<div>
function myFunction(<em><span class="marked">var1</span></em>,<em><span class="marked">var2</span></em>)<br>{<br><em>代码</em><br>
}</div>
</div>
<p>变量和参数必须以一致的顺序出现。第一个变量就是第一个被传递的参数的给定的值,以此类推。</p>
<p>在进行函数调用的时候,不管实参的数目大于形参还是小于形参,被调用的函数都会执行;在JS中函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型。发生函数调用的时候可以给一个实参也可以给多个实参,之所以会这样,是因为在js中的参数在内部是用一个数组来表示。函数接收到的始终是这个数组,而不关心数组中包含哪些参数,如果这个数组不包含任何参数也无所谓,包含多个参数也没问题,在函数体内可以通过arguments(参数)对象来访问这个参数数组,从而获取传递给参数的每个参数。 arguments对象和数组对象相类似,可以通过下标来获取传入的每一个元素(第一个元素是arguments);也可以使用length属性来确定传递进来多少个参数,arguments对象的长度是由传入的参数个数决定,不是由定义函数的参数的个数决定</p>
<h2>四种调用函数的方式及this指向</h2>
<p><span style="font-size: 16px; font-family: Microsoft YaHei"><strong>1、函数调用模式,<strong>this指向的全局对象window.</strong></strong></span></p>
<p>实例1:</p>
<p>函数声明</p>
<div class="cnblogs_Highlighter sh-gutter">
<div>
<div id="highlighter_542105" class="syntaxhighlighterjavascript">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="gutter">
<div class="line number1 index0 alt2">1</div>
<div class="line number2 index1 alt1">2</div>
<div class="line number3 index2 alt2">3</div>
<div class="line number4 index3 alt1">4</div>
<div class="line number5 index4 alt2">5</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2"><code class="javascript keyword">function</code> <code class="javascript plain">add(a,b) {</code></div>
<div class="line number2 index1 alt1"><code class="javascript spaces"> </code><code class="javascript keyword">return</code> <code class="javascript plain">a+b;</code></div>
<div class="line number3 index2 alt2"><code class="javascript plain">}</code></div>
<div class="line number4 index3 alt1"> </div>
<div class="line number5 index4 alt2"><code class="javascript plain">console.log(add(1,2));</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p>实例2:</p>
<p>函数表达式</p>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="gutter">
<div class="line number1 index0 alt2">1</div>
<div class="line number2 index1 alt1">2</div>
<div class="line number3 index2 alt2">3</div>
<div class="line number4 index3 alt1">4</div>
<div class="line number5 index4 alt2">5</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2"><code class="javascript keyword">var</code> <code class="javascript plain">add=</code><code class="javascript keyword">function</code> <code class="javascript plain">(a,b) {</code></div>
<div class="line number2 index1 alt1"><code class="javascript spaces"> </code><code class="javascript keyword">return</code> <code class="javascript plain">a+b;</code></div>
<div class="line number3 index2 alt2"><code class="javascript plain">}</code></div>
<div class="line number4 index3 alt1"> </div>
<div class="line number5 index4 alt2"><code class="javascript plain">console.log(add(1,2));</code></div>
</div>
</td>
</tr>
</tbody>
</table>
<p><span style="font-size: 16px; font-family: Microsoft YaHei"><strong>2、方法调用模式,<strong>this指向当前的对象</strong></strong></span></p>
<div class="container">
<div class="line number1 index0 alt2"><code class="javascript keyword">var</code> <code class="javascript plain">o = {</code></div>
<div class="line number2 index1 alt1"><code class="javascript spaces"> </code><code class="javascript plain">prop: 21,</code></div>
<div class="line number3 index2 alt2"><code class="javascript spaces"> </code><code class="javascript plain">f:</code><code class="javascript keyword">function</code><code class="javascript plain">() {</code></div>
<div class="line number4 index3 alt1"><code class="javascript spaces"> </code><code class="javascript keyword">return</code> <code class="javascript keyword">this</code><code class="javascript plain">.prop;</code></div>
<div class="line number5 index4 alt2"><code class="javascript spaces"> </code><code class="javascript plain">}</code></div>
<div class="line number6 index5 alt1"><code class="javascript plain">};</code></div>
<div class="line number8 index7 alt1"><code class="javascript plain">console.log(o.f());</code></div>
</div>
<p><span style="font-size: 16px; font-family: Microsoft YaHei"><strong>3、构造器模式,this指向新创建的实例对象,也就是这里的o</strong></span></p>
<div class="container">
<div class="line number1 index0 alt2"><code class="javascript keyword">function</code> <code class="javascript plain">MyClass() {</code></div>
<div class="line number2 index1 alt1"><code class="javascript spaces"> </code><code class="javascript keyword">this</code><code class="javascript plain">.a = 21;</code></div>
<div class="line number3 index2 alt2"><code class="javascript plain">}</code></div>
<div class="line number5 index4 alt2"><code class="javascript keyword">var</code> <code class="javascript plain">o = </code><code class="javascript keyword">new</code> <code class="javascript plain">MyClass();</code></div>
<div class="line number6 index5 alt1"><code class="javascript plain">console.log(o.a);</code><code class="javascript comments">//21</code></div>
</div>
<p><span style="font-size: 16px; font-family: Microsoft YaHei"><strong>4、apply\call模式,<strong>这两个的第一参数即this,当第一个参数为null,this指向window;当第一个参数为一个对象,this就指向当前这个对象。</strong></strong></span></p>
<p>call和apply的作用一样,只是接受参数的方式不一样,call接受的是多个单个参数,apply接受的是参数数组。<br>
call和apply的作用简单地可以说成,当一个对象实例缺少一个函数/方法时,可以调用其他对象的现成函数/方法,其方式是通过替换其中的this为这个对象实例,改变函数运行时的上下文。call方法的妙用:1、继承 2、改变函数运行时的this指针<br>
例如:</p>
<pre><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">Dog(<span class="hljs-params">){
<span class="hljs-keyword">this.sound=<span class="hljs-string">"汪汪汪";
}
Dog.prototype.bark=<span class="hljs-function"><span class="hljs-keyword">function(<span class="hljs-params">){
alert(<span class="hljs-keyword">this.sound);
}</span></span></span></span></span></span></span></span></span></span></code></pre>
<p>现在我有另外一个cat对象:</p>
<pre><code class="hljs javascript"><span class="hljs-keyword">var cat={<span class="hljs-attr">sound:<span class="hljs-string">'喵喵喵'}</span></span></span></code></pre>
<p>我也想让这个cat对象可以调用bark方法,这时候就不用重新为它定义bark方法了,可以用call/apply调用Dog类的bark方法:</p>
<pre><code class="hljs css"><span class="hljs-selector-tag">Dog<span class="hljs-selector-class">.prototype<span class="hljs-selector-class">.bark<span class="hljs-selector-class">.call(<span class="hljs-selector-tag">cat);</span></span></span></span></span></code></pre>
<p>或者:</p>
<pre><code class="hljs css"><span class="hljs-selector-tag">dog<span class="hljs-selector-class">.bark<span class="hljs-selector-class">.call(<span class="hljs-selector-tag">cat);</span></span></span></span></code></pre>
<p>加点东西,变成一个带参数的栗子:</p>
<pre><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">Dog(<span class="hljs-params">){
<span class="hljs-keyword">this.sound=<span class="hljs-string">"汪汪汪";
}
Dog.prototype.bark=<span class="hljs-function"><span class="hljs-keyword">function(<span class="hljs-params">words){
alert(<span class="hljs-keyword">this.sound+<span class="hljs-string">""+words);
}
<span class="hljs-keyword">var dog=<span class="hljs-keyword">new Dog();
dog.bark(<span class="hljs-string">"有小偷");<span class="hljs-comment">//alert:汪汪汪 有小偷
Dog.prototype.bark.call(cat,<span class="hljs-string">"饿了");<span class="hljs-comment">//alert:喵喵喵 饿了<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h2>普通函数与构造函数的区别</h2>
<p><span style="font-size: 16px; font-family: Microsoft YaHei"><strong><strong>普通函数与构造函数相比有四个明显特点:</strong></strong></span></p>
<p>1.不需要用new关键字调用</p>
<pre><code class="hljs less"> <span class="hljs-selector-tag">fn();</span></code></pre>
<p>2.可以用return语句返回值</p>
<pre><code class="hljs delphi"> <span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">fn<span class="hljs-params">(a,b)<span class="hljs-comment">{
return a+b;
}
<span class="hljs-title">alert<span class="hljs-params">(fn(2,3));<span class="hljs-comment">//alert:5</span></span></span></span></span></span></span></span></code></pre>
<p>3.函数内部不建议使用this关键字<br>
我们说不建议使用,当然硬要用是可以的,只是要注意这时候发生了什么。如果在普通函数内部使用this关键字定义变量或函数,因为这时候this指向的是window全局对象,这样无意间就会为window添加了一些全局变量或函数。</p>
<pre><code class="hljs javascript"> <span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">greeting(<span class="hljs-params">){
<span class="hljs-keyword">this.name=<span class="hljs-string">"sheila";
alert(<span class="hljs-string">"hello "+<span class="hljs-keyword">this.name);
}
greeting();<span class="hljs-comment">//alert:hello sheila
alert(<span class="hljs-built_in">window.name);<span class="hljs-comment">//alert:sheila</span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>4.函数命名以驼峰方式,首字母小写</p>
<p><span style="font-size: 16px; font-family: Microsoft YaHei"><strong><strong><span style="font-size: 16px; font-family: Microsoft YaHei"><strong><strong><span style="font-size: 16px; font-family: Microsoft YaHei"><strong><strong>与</strong></strong></span></strong></strong></span>普通函数相比,<span style="font-size: 16px; font-family: Microsoft YaHei"><strong><strong><span style="font-size: 16px; font-family: Microsoft YaHei"><strong><strong>构造函数</strong></strong></span></strong></strong></span>有以下明显特点:</strong></strong></span></p>
<p>1.用new关键字调用</p>
<pre><code class="hljs javascript"> <span class="hljs-keyword">var prince=<span class="hljs-keyword">new Prince(<span class="hljs-string">"charming",<span class="hljs-number">25);</span></span></span></span></code></pre>
<p>2.函数内部可以使用this关键字<br>
在构造函数内部,this指向的是构造出的新对象。用this定义的变量或函数/方法,就是实例变量或实例函数/方法。需要用实例才能访问到,不能用类型名访问。</p>
<pre><code class="hljs javascript"> prince.age;<span class="hljs-comment">//25
Prince.age;<span class="hljs-comment">//undefined</span></span></code></pre>
<p>3.默认不用return返回值<br>
构造函数是不需要用return显式返回值的,默认会返回this,也就是新的实例对象。当然,也可以用return语句,返回值会根据return值的类型而有所不同。</p>
<p>构造函数里调用return时,分两种情况:</p>
<p>3.1 return的是五种简单数据类型:String,Number,Boolean,Null,Undefined。<br>
这种情况下,忽视return值,依然返回this对象。</p>
<p>3.2 return的是Object<br>
这种情况下,不再返回this对象,而是返回return语句的返回值。</p>
<pre><code class="hljs javascript"> <span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">Person(<span class="hljs-params">name){
<span class="hljs-keyword">this.name=name;
<span class="hljs-keyword">return {<span class="hljs-attr">name:<span class="hljs-string">"cherry"}
}
<span class="hljs-keyword">var person=<span class="hljs-keyword">new Person(<span class="hljs-string">"sheila");
person.name;<span class="hljs-comment">//cherry
person;<span class="hljs-comment">//Object {name: "cherry"}</span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>4.函数命名建议首字母大写,与普通函数区分开。<br>
不是命名规范中的,但是建议这么写。</p>
<h2 id="使用new关键字实例化的时候发生了什么">使用new关键字实例化的时候发生了什么?</h2>
<p>以上文中的Prince()函数举个栗子:</p>
<p>1.第一步,创建一个空对象。</p>
<pre><code class="hljs javascript"><span class="hljs-keyword">var prince={}</span></code></pre>
<p>2.第二步,将构造函数Prince()中的this指向新创建的对象prince。<br>
3.第三步,将prince的<code>_proto_</code>属性指向Prince函数的prototype,创建对象和原型间关系<br>
4.第四步,执行构造函数Prince()内的代码</p>
<h2>原型对象</h2>
<p> function Person(name){<br> this.name = name;<br>}</p>
<p><img src="https://img2018.cnblogs.com/blog/1335963/201907/1335963-20190703151800924-397225777.png"></p>
<p>(google浏览器)</p>
<p>如上,当我们创建一个函数,系统就会为这个函数自动分配一个prototype指针,指向它的原型对象。并且可以发现,这个原型对象包含两个部分(constructor 和 __proto__)其中constructor指向函数自身。<span style="color: rgba(204, 153, 255, 1)">(这里形成了一个小闭环)</span></p>
<p>var p = new Person("jack");<br>console.log(p);</p>
<p><img src="https://img2018.cnblogs.com/blog/1335963/201907/1335963-20190703152229100-2133208043.png"></p>
<p><img src="https://img2018.cnblogs.com/blog/1335963/201907/1335963-20190703152248533-560105001.png"></p>
<p> </p>
<p>我们将该函数作为模版创建实例(new方法)的时候,我们发现创建出的实例是一个与构造函数同名的object,这个object是独立的,也就是这里的p,他只包含了一个__proto__指针(实例没有prototype,强行访问则会输出undefined),这个指针指向上面提到的构造函数的prototype原型对象,则可以得出:p.__proto__===Person.prototype</p>
<p>结果如下:</p>
<p><img src="https://img2018.cnblogs.com/blog/1335963/201907/1335963-20190703162504349-1042151353.png"></p>
<p> 在查看下Object的原型对象:</p>
<p><img src="https://img2018.cnblogs.com/blog/1335963/201907/1335963-20190703181812017-118874706.png"></p>
<p>通过上面的代码可以看到:</p>
<ul>
<li>Object对象本身是一个函数对象。</li>
<li>既然是Object函数,就肯定会有prototype属性,所以可以看到"Object.prototype"的值就是"Object {}"这个原型对象。</li>
<li>反过来,当访问"Object.prototype"对象的"constructor"这个属性的时候,就得到了Obejct函数。</li>
<li>另外,当通过"Object.prototype.__proto__"获取Object原型的原型的时候,将会得到"null",也就是说"Object {}"原型对象就是原型链的终点了</li>
</ul>
<p>在查看下Function的对象:</p>
<p><img src="https://img2018.cnblogs.com/blog/1335963/201907/1335963-20190703184241351-1581044389.png"></p>
<p>结果分析 :</p>
<ul>
<li>在JavaScript中有个Function对象(类似Object),这个对象本身是个函数;所有的函数(包括Function,Object)的原型(__proto__)都是"Function.prototype"。</li>
<li>Function对象作为一个函数,就会有prototype属性,该属性将对应"function () {}"对象。</li>
<li>Function对象作为一个对象,就有"__proto__"属性,该属性对应"Function.prototype",也就是说,"Function.__proto__ === Function.prototype"</li>
<li>对于Function的原型对象"Function.prototype",该原型对象的"__proto__"属性将对应"Object {}"</li>
</ul>
<p>在这里对"prototype"和"__proto__"进行简单的介绍:</p>
<ul>
<li>对于所有的对象,都有__proto__属性,这个属性对应该对象的原型</li>
<li>对于函数对象,除了__proto__属性之外,还有prototype属性,<strong>当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)</strong>。也就是Person.prototype===p.__proto__</li>
</ul>
<p>1、每个函数都有prototype属性,这个属性指向函数的原型对象</p>
<p>2、所有对象的__proto__都指向其构造器(constructor)的prototype</p>
<p>举个栗子:</p>
<p>function Person(name){</p>
<p> this.name = name;</p>
<p>}</p>
<p>var p = new Person("jack");</p>
<p>console.log(p.__proto__===Person.prototype);//true</p>
<p>console.log(p.__proto__===p.constructor.prototype);//true</p>
<p>3、构造函数、实例和原型对象的区别与联系</p>
<p>3.1 实例就是通过构造函数创建的。实例创造出来就具有constructor属性(指向构造函数)和__proto__属性(指向原型对象)</p>
<p>3.2 构造函数中有一个prototype属性。这个属性是一个指针,指向它的原型对象</p>
<p>3.3 原型对象内部也有一个指针(constructor)指向构造函数。Person.prototype.constructor=Person;</p>
<p>3.4 Person.prototype.__proto__语句获取原型对象的原型,将得到Object对象。所有对象的原型都将追溯到Object对象。</p>
<p>Person.prototype.__proto__ === Object.prototype; //true</p>
<p><img src="https://img2018.cnblogs.com/blog/1335963/201907/1335963-20190703180205204-1138484394.png"></p>
<p> </p>
<h2>最后上一个图,便于理解</h2>
<p> <img src="https://img2018.cnblogs.com/blog/1335963/201907/1335963-20190703171735078-283801655.png"></p>
<p> </p>
<p><span style="font-size: 16px; font-family: Microsoft YaHei"><strong><strong><span style="font-size: 16px; font-family: Microsoft YaHei"><strong> </strong></span></strong></strong></span></p><br><br>
来源:https://www.cnblogs.com/g012/p/11115716.html
頁:
[1]