[javascript ECMAScript5 メモ] get,set

A.
EMCAScript5では、ゲッター、セッターが追加されました。
以下のような書式で記述します。
// Object Literal
console.log("Object Literal ---");
var obj = { _id: 1,
            get id() {
              return this._id;
            },
            set id(value) {
          if( isNaN(value) ) throw new Error("Not Number");
              this._id = value;
            }
          };
console.log( obj.id );
obj.id = 2;
console.log( obj.id );
console.log( Object.keys( obj ) );


// Object.defineProperty
console.log("Object.defineProperty ---");
var obj2 = {
  _id: 1
}

Object.defineProperty(obj2, "id", {
  enumerable: false,
  configurable: false,
  get : function(){
          return this._id;
        },
  set : function(value){
      if( isNaN(value) ) throw new Error("Not Number");
          this._id = value;
        }
});
console.log( obj2.id );
obj2.id = 2;
console.log( obj2.id );
console.log( Object.keys( obj2 ) );

// Private
console.log("Private ---");
var obj3 = {};
(function () {
  var _id = 1;
  Object.defineProperty(obj3, "id", {
    enumerable: false,
    configurable: false,
    get : function(){
            return _id;
          },
    set : function(value){
      if( isNaN(value) ) throw new Error("Not Number");
            _id = value;
          }
  });

})();
console.log( obj3.id );
obj3.id = 2;
console.log( obj3.id );
console.log( Object.keys( obj3 ) );
js_e5_sg1.png

参考サイト
MDN get
MDN set
ECMA-262 ECMA-262-5 in detail. Chapter 1. Properties and Property Descriptors.

[javascript ECMAScript5] オブジェクトのclone

A.
EMCAScript5では、オブジェクトをcloneする時にその属性にも気をつける必要がある。

hachy.net ECMAScript 5 に追加された新しい関数(Object編)さんのサイトを参考に試してみる。
// add clone function
Object.clone = function (o) {
    var n = Object.create(Object.getPrototypeOf(o));
    var props = Object.getOwnPropertyNames(o);
    var pName;
    for (var p in props) {
        pName = props[p];
        Object.defineProperty(n, pName, Object.getOwnPropertyDescriptor(o, pName));
    };
    return n;
};

// clone test
var obj = Object.create(Object.prototype, {
  id: {
    value: 1,
    configurable: true,
    writable: false,
    enumerable: true
  },
  nickname: {
    enumerable: true,
    configurable: false,
    get : function(){
            console.log("get");
            return this._nickname
          },
    set : function(value){
            console.log("set");
            this._nickname = value
          }
  }
});

console.dir( obj );
console.log( Object.getOwnPropertyDescriptor(obj, "id") );
console.log( Object.getOwnPropertyDescriptor(obj, "nickname") );

console.log("clone+++++++++++++++++");

var obj2 = Object.clone( obj );
console.dir( obj2 );
console.log( Object.getOwnPropertyDescriptor(obj2, "id") );
console.log( Object.getOwnPropertyDescriptor(obj2, "nickname") );
実行結果
js_ecma5_clone_1.png
内部属性も同様な状態でコピーできている事が確認できる。

参考サイト
hachy.net ECMAScript 5 に追加された新しい関数(Object編)
IEBlog ECMAScript 5 Part 1: Reusable Code
A.
Property Descriptorsまわりの動作メモ

1. Object.create Property Descriptorデフォルト値の確認
Object.create時にvalueだけ設定した場合configurable,enumerable,writable の値を確認する
var obj = Object.create(Object.prototype, {
  id: { value: 1 }
});
console.log( Object.getOwnPropertyDescriptor(obj, "id") );
console.dir( obj );
結果(Safari 5.0.3 Mac)
jd_pd_1.png

2. オブジェクトリテラルで作成した場合のProperty Descriptorデフォルト値の確認
var obj = {id: 1};
console.log( Object.getOwnPropertyDescriptor(obj, "id") );
console.dir( obj );
結果(Safari 5.0.3 Mac)
js_pd_2.png

3.ゲッター、セッターを使う場合 value, writableは設定できない
get, setとvalueを同時に設定してみる

var obj = Object.create(Object.prototype, {
  id: {
    value: 1,
    enumerable: true,
    configurable: true,
    get : function(){
            return this._id;
          },
    set : function(value){
            this._id = value;
          },
  }
});
結果(Safari 5.0.3 Mac)
js_pd_3.png

get, setとwritableを同時に設定してみる
var obj = Object.create(Object.prototype, {
  id: {
    writable : true,
    enumerable: true,
    configurable: true,
    get : function(){
            return this._id;
          },
    set : function(value){
            this._id = value;
          },
  }
});
結果(Safari 5.0.3 Mac)
js_pd_4.png

4. preventExtensionsで新たにプロパティが追加できなくなるか?
var obj = Object.create(Object.prototype, {
  id: { value: 1,
        configurable: true,
        writable: true,
        enumerable: true
      }
});

// preventExtensionsを設定
console.log( Object.isExtensible( obj ) );
Object.preventExtensions( obj );
console.log( Object.isExtensible( obj ) );

// definePropertyは可能か
Object.defineProperty(obj, "id", {
  configurable: false
});

// 既存のプロパティの値の変更は可能か
console.log( obj.id );
obj.id = 2;
console.log( obj.id );

// 新しくプロパティを追加できるか?
obj.nickname = "max";
/*
Object.defineProperty(obj, "nickname", {
  value : 1
});
*/
結果(Chrome 10.0.648.126 Mac)
js_pd_5.png

5. sealでpreventExtentionsとconfigurableがどのように変わるか
var obj = Object.create(Object.prototype, {
  id: { value: 1,
        configurable: true,
        writable: true,
        enumerable: true
      }
});

// preventExtensionsを設定
console.log( Object.isExtensible( obj ) );
console.log( Object.isSealed( obj ) );
Object.seal( obj );
console.log( Object.isExtensible( obj ) );
console.log( Object.isSealed( obj ) );


// definePropertyは可能か
console.log( Object.getOwnPropertyDescriptor(obj, "id") );
Object.defineProperty(obj, "id", {
  configurable: true
});
console.log( Object.getOwnPropertyDescriptor(obj, "id") );


// 既存のプロパティの値の変更は可能か
console.log( obj.id );
obj.id = 2;
console.log( obj.id );

// 新しくプロパティを追加できるか?
obj.nickname = "max";
結果(Chrome 10.0.648.126 Mac)
js_pd_6.png

6. freezeでpreventExtentionsとconfigurable,writableがどのように変わるか
  id: { value: 1,
        configurable: true,
        writable: true,
        enumerable: true
      }
});

// preventExtensionsを設定
console.log( Object.isExtensible( obj ) );
console.log( Object.isSealed( obj ) );
console.log( Object.isFrozen( obj ) );
Object.freeze( obj );
console.log( Object.isExtensible( obj ) );
console.log( Object.isSealed( obj ) );
console.log( Object.isFrozen( obj ) );

console.log( Object.getOwnPropertyDescriptor(obj, "id") );

// definePropertyは可能か
/*
console.log( Object.getOwnPropertyDescriptor(obj, "id") );
Object.defineProperty(obj, "id", {
  configurable: true
});
console.log( Object.getOwnPropertyDescriptor(obj, "id") );
*/

// 既存のプロパティの値の変更は可能か
console.log( obj.id );
obj.id = 2;
console.log( obj.id );
結果(Chrome 10.0.648.126 Mac)
js_pd_7.png

参考になるサイト
15.2.3.8 Object.seal ( O )
15.2.3.9 Object.freeze ( O )
ECMA-262-5 in detail. Chapter 1. Properties and Property Descriptors.
ECMAScript 5:The Definitive Slides
prog*sig Chapter 16. JavaScript Objects
MSDN Object.defineProperty Function (JavaScript)
ECMA Script 5 の Property Descriptor について話してきました
ECMAScript 5 に追加された新しい関数(Object編)
枕を欹てて聴く ES5, Property Descriptor解説

[javascript DOM] Q. ノードを操作するには?

A.
createElementでノードオブジェクトの作成を行い
要素ノード.appendChild(追加したいノードオブジェクト)
で追加します。

ノードの操作に関するメンバ
プロパティ名 説明
要素ノード.appendChild(追加したいノードオブジェクト) 要素ノードの子ノードの最後に追加します
要素ノード.insertBefore(追加したいノードオブジェクト, 直前に挿入したい対象のノード) 第1引数で指定したノードオブジェクトを第2引数で指定したノードの前に追加します
要素ノード.replaceChild(ノード, 置換対象ノード) 要素ノードの子ノードを第2引数に指定したノードを第1引数のノードと置換します
要素ノード.removeChild(削除したいノード) 指定した子ノードを削除します

実際に試してみましょう。
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>DOM</title>
  <script type="text/javascript">

  // メッセージを表示するdivを用意する
  window.onload = function() {
    var messages = document.createElement('div');
    messages.id = "messages"; // id属性を設定
    messages.className = "messages"; // class属性を設定
    messages.innerHTML = "メッセージの表示<br />";
    messages.style.backgroundColor = '#ccc'; // styleを設定
    document.getElementsByTagName("body")[0].appendChild(messages);
  }

  // クリアを押した時の処置
  function clearMessage() {
    if( confirm("本当に削除しますか?") ){
      while (messages.firstChild) {
        messages.removeChild(messages.firstChild);
      }
    }
  }

  // 送信ボタンを押した時の処理
  function sendMessage(){
    // メッセージを表示するノードを取得する
    console.dir(messages.childNodes);

    // テキストノードを作成
    var message = document.createTextNode(messageform.message.value);
    messages.appendChild(message);

    // br を作成
    var br = document.createElement('br');
    messages.appendChild(br);

    // inputを空にする
    messageform.message.value = '';

    return false;
  }
  </script>
  <style type="text/css">
  #messages {
    border: 1px solid #000;
  }
  .messages {
    width: 500px;
    height: 100px;
  }
  </style>
</head>
<body>
<form action="./" method="get" name="messageform" id="messageform" onsubmit="return sendMessage()">
   <label>ひとことめっせーじ: <input type="text" name="message" id="messgae" size="43"></label>
  <input type="submit" value="送信"><input id="clear" type="button" value="クリア" onclick="clearMessage()">
</form>
</body>
</html>
js_dom_s3.png

参考サイト
MDN document.createElement
MDN Node.appendChild
W3C DOM 2 Core: createElement
A. 属性にアクセスする方法として一番簡単な方法は、
要素ノードのプロパティとしてアクセスする方法です。

要素ノード.属性の名前;
要素ノード.属性の名前 = 属性の値;
*classはclassNameでアクセスする

要素の属性に関するメンバ
プロパティ名 説明
getAttribute() 要素の属性を取得
setAttribute() 要素の属性を設定
removeAttribute() 要素の属性を削除
createAttribute() 属性ノードを設定
atttributes すべての属性を取得
hasAttribute() 要素に属性が設定されてるか調べる
属性ノード.nodeName 属性の名称を取得
属性ノード.nodeValue 属性の値を取得
属性ノード.nodeType 2:属性ノード


実際に試してみる
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>DOM</title>
  <script type="text/javascript">
  window.onload = function() {
    // 基準となるノードを取得する
    var example = document.getElementById('example');
    console.log( "// document.getElementById('example')" );
    console.dir(example.attributes);

    console.log( "\n// example.attributes.length" );
    console.dir(example.attributes.length);
    var length = example.attributes.length;

    console.log( "\n// nodeValusで取得" );
    for(var i = 0; i < length; i++){
      // attributes[配列インデックス].nodeValue
      console.log( example.attributes[i].nodeName + ":"
                   + example.attributes[i].nodeValue );
    }

    // NamedNodeMapオブジェクト.item(インデックス番号)
    console.log( "\n// NamedNodeMapオブジェクト.item(インデックス番号)" );
    console.log( example.attributes.item(1) );

    // NamedNodeMapオブジェクト.getNamedItem('属性名');
    console.log( "\n// NamedNodeMapオブジェクト.getNamedItem('属性名')" );
    console.log( example.attributes.getNamedItem('src').value );

    // 要素ノード.属性の名前で取得
    console.log( "\n// 要素ノード.属性の名前で取得" );
    console.log( example.src );

    // getAttribute('属性の名前')で取得
    console.log( "\n// getAttribute('属性の名前')で取得" );
    console.log( example.getAttribute('src') );

    // 要素ノード.属性の名前 = 属性の値で設定
    /*console.log( "\n// 要素ノード.属性の名前 = 属性の値で設定" );
    example.src = 'http://memo.yoshimax.net/assets_c/2011/02/js_dom_node_w3-thumb-421x672-176.png';
    example.width = "421";
    example.height = "672"; */

    // setAttribute('属性の名前', '属性の値')で設定
    console.log( "\n// setAttribute('属性の名前', '属性の値')で設定" );
    example.setAttribute('src', 'http://memo.yoshimax.net/assets_c/2011/02/js_dom_node_w3-thumb-421x672-176.png');
    example.setAttribute('width', 421);
    example.setAttribute('height', 672);
  };
  </script>
</head>
<body>
<img alt="js_prototype_proto_1.png" src="http://memo.yoshimax.net/assets_c/2011/02/js_prototype_proto_1-thumb-544x454-140.png" width="544" height="454" class="mt-image-none" style="" id="example" />
</body>
</html>

コンソールログ
js_dom_get_set_1.png

参考サイト:
三等兵 JavaScriptのDOM Core基礎
DOM Level 2 Core: getAttribute
DOM Level 1 Core
HTML 5: APIs in HTML documents
MDN Nodeのメンバー一覧
MDN NamedNodeMapのメンバー
A.
DOMでは、getElementByIdなどで直接ノードを取得する以外にも
相対的にノードを取得する事が可能です。このように相対的に
ノードを取得する方法をノードウォーキング言います。

ノードウォーキングでよく使われるNodeオブジェクトのメンバーを確認してみます。

ノードウォーキングで使われるメンバー
プロパティ名 説明
firstChild 最初の子ノードを取得
lastChild 最後の子ノードを取得
perviousSibling 直前のノードを取得
nextSibling 直後のノードを取得
childNodes.length 子ノードに含まれるノード数を取得
parentNode 親ノードを取得


これらのNodeオブジェクトのメンバーを図に表すと次のようになります。
js_dom_node_w1.png

ここで実際のHTMLをみて確認してみます。
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>DOM childNodes</title>
  <script type="text/javascript">
  window.onload = function() {
    // 基準となるノードを取得する
    var ulnode = document.getElementById('item');
    console.dir(ulnode.childNodes);

    // 子ノードの1番最初を取得
    var first_child = ulnode.firstChild;
    console.dir(first_child);

    // 子ノードの2番目ノードを取得
    var next = first_child.nextSibling;
    console.dir(next);

    // 子ノードの一番最後のノードを取得
    var last_child = ulnode.lastChild;
    console.dir(last_child);

    // 子のノードの最後から2番目を取得
    var	before = last_child.previousSibling;
    console.dir(before);
  };
  </script>
  <style type="text/css">
  ul, li, div {
    border: 1px solid #f00;
    padding: 5px;
  }
  </style>
</head>
<body>
<h1>一覧</h1>
<div id="list">
  <ul id="item">
    <li>アイテム1</li>
    <li>アイテム2</li>
    <li>アイテム3</li>
  </ul>
</div>
</body>
</html>
実行結果は次のようになります。
js_dom_node_w3.png

実行結果を確認すると子ノードの1番最初を取得するとTextになっています。
ここでHTMLを修正してもう一度確認してみます。
ulのインデントを無くして一行で記述します。
<body>
<h1>一覧</h1>
<div id="list"><ul id="item"><li>アイテム1</li><li>アイテム2</li><li>アイテム3</li></ul></div>
</body>
</html>
実行結果は次のようになります。
js_dom_node_w4.png
HTMLをインデントした場合タグとタグの間の改行や空白も
Nodeリストに入るのでノードウォーキングの際は注意が必要です。

この影響を考慮するのはnodeTypeの確認を行いnodeType=1の要素ノードの時のみ処理を行ったり
nodeType=3テキストノードを取り除いたりします。

このような影響があるのでノードウォーキングは扱いにくかったのですが、
Element Traversalと言うAPIが定義されました。

Element Traversalを確認してみましょう
ElementTraversal API
プロパティ名 説明
firstElementChild 最初の子要素ノードを取得
lastElementChild 最後の子要素ノードを取得
perviousElementSibling 直前の要素ノードを取得
nextElementSibling 直後の要素ノードを取得
childElementCount 子ノードに含まれる要素ノード数を取得
parentNode 親ノードを取得

図に表すと以下のようになります。
js_dom_node_w5.png
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>DOM childNodes</title>
  <script type="text/javascript">
  window.onload = function() {
    // 基準となるノードを取得する
    var ulnode = document.getElementById('item');

    // childNode
    console.dir(ulnode.childNodes);

    // children
    console.dir(ulnode.children);

    // 子ノードの1番最初を取得
    var first_child = ulnode.firstElementChild;
    console.dir(first_child);

    // 子ノードの2番目ノードを取得
    var next = first_child.nextElementSibling;
    console.dir(next);

    // 子ノードの一番最後のノードを取得
    var last_child = ulnode.lastElementChild;
    console.dir(last_child);

    // 子のノードの最後から2番目を取得
    var before = last_child.previousElementSibling;
    console.dir(before);
  };
  </script>
  <style type="text/css">
  ul, li, div {
    border: 1px solid #f00;
    padding: 5px;
  }
  </style>
</head>
<body>
<h1>一覧</h1>
<div id="list">
  <ul id="item">
    <li>アイテム1</li>
    <li>アイテム2</li>
    <li>アイテム3</li>
    </ul>
  </div>
</body>
</html>
js_dom_node_w6.png
要素ノードのみを取得する事が可能な事が確認できました。

対応ブラウザは、以下のサイトから確認できます。
W3C DOM Compatibility - Traversal

参考サイト:
XML DOM Node Types
MDN Element.children
三等兵 Element Traversal API
A.
DOMはHTMLをツリー構造で表します。
以下のHTMLのツリー構造をみてみましょう。
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>TITILE</title>
  <script type="text/javascript">
  var log = "";

  window.onload = function() {
    getChild( document.getElementsByTagName('body')[0], 0 );
//    getChild( document.getElementsByTagName('head')[0], 0 );
    console.log(log);
  };

  function getChild( nodes, lv ){
    for(var num in nodes.childNodes){
      if(nodes.childNodes[num].nodeName && nodes.childNodes[num].nodeType != 3){
         log += Level(lv) + nodes.childNodes[num].nodeName + "\n";
        if( nodes.childNodes[num].length != 0 ) getChild( nodes.childNodes[num], lv+1 );
      }
    }
  }

  function Level(vl){
    var mes = '';
    while(vl--) mes += '-';
    return mes;
  }
  </script>
</head>
<body>
<h1>一覧</h1>
<ul>
  <li>アイテム1</li>
  <li>アイテム2</li>
  <li>アイテム3</li>
</ul>
<form action="./" method="get" name="userform" id="userform" onsubmit="return confirm();">
   <label>ニックネーム: <input type="text" name="nickname" id="nickname"></label>
  <input type="submit" value="送信">
</form>
</body>
</html>
nodeType=3以外のノード
js_dom_t1.png

これらのログからDOMのツリー構造を図で表してみます。

DOMのツリー構造の図
js_dom_t2.png ブラウザによって、DOMの解釈が異なります。
タクとタグの間にある空白や改行が無視されるかは
最も大きな違いとして認識されています。

参考サイト:
三等兵 jsのDOMによるノード取得
MDN Whitespace in the DOM
JavaScriptのDOM Core基礎

[javascript] DOMLevel0とは?

A. ブラウザ上で動くJavaScriptでは、HTMLの要素にアクセスし
データの取得や変更を行いたい事があります。

現在のブラウザでは、HTMLを解析しDOMという形でアクセスを行う事ができます。
しかしDOMが実装される前にも独自の方法でHTMLの要素にアクセスが行えました。
この昔からのアクセス方法はDOM以前の方式という意味から
レガシーDOMまたはDOMLevel0という呼び方で呼ばれています。

DOMではHTMLの要素すべてがアクセスできるのに対して、
レガシーDOMではアクセスできる要素が限られています、

フォーム要素、img要素、リンク要素などにアクセス可能です。
レガシーDOMは、現在でもフォーム要素のバリデーションの為に
利用されている場合もあるので確認して起きましょう。

ブラウザのオブジェクト
js_domlevel0_1.png

ここで実際フォームの要素にアクセスの方法を確認します。
レガシーDOMを利用する場合フォーム要素のname属性を使い名前をつける事が重要です。
この名前を利用し、要素へのアクセスを行います。
1. document.forms["フォーム名"].elements["要素名"]
2. document.forms[インデックス番号].elements[インデックス番号]
3. document.フォーム名.要素名
4. document.['フォーム名'].['要素名']
ここでinputフォームにアクセスを確認してみます。
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script type="text/javascript">
  function confirm() {
    //  document.forms["フォーム名"].elements["要素名"]
    var nick = document.forms["userform"].elements["nickname"].value;

    // document.forms[インデックス番号].elements[インデックス番号]
    var nick2 = document.forms[0].elements[0].value;

    // document.フォーム名.要素名
    var nick3 = document.userform.nickname.value;

    // document.['フォーム名'].['要素名']
    var nick4 = document['userform']['nickname'].value;

    console.dir(nick);
    console.dir(nick2);
    console.dir(nick3);
    console.dir(nick4);
    return false;
  }
  </script>
</head>
<body>
<form action="./" method="get" name="userform" id="userform" onsubmit="return confirm();">
   <label>ニックネーム: <input type="text" name="nickname" id="nickname"></label>
  <input type="submit" value="送信">
</form>
</body>
</html>
コンソールログの値
js_form_t1.png

このようにinput要素の値を取り出せる事が確認できました。
取り出した要素にどのようなプロパティやメソッドがあるのかを調べたい場合
レガシーDOMでは、共通な仕様はありませんでしたが、
HTML5の取り組みとして新たにHTML5 specificationとして規定されました。

どの要素にどのようなプロパティやメソッドがあるかのAPIの確認は
HTML5 specification the-input-elementをまず確認しておくと良いかもしれません。

Safari Windowオブジェクトメンバー一覧
Safari form inputオブジェクトメンバー一覧
Safari imagesオブジェクトメンバー一覧

参考サイト
Web Forms 2.0
HTML5 specification
DOM Level0
mozilla Web 開発者資料
NetScape Client-Side JavaScript Reference

WHATWG 4.8.1 The img element
WHATWG 4.10 Forms
A.
オブジェクトを複製する際にオブジェクト内部のプロパティに参照型の値設定されている場合
参照のままコピーするのをシャローコピー(浅いコピー)といい、内部のプロパティまでコピーするのを
ディープコピー(深いコピー)といいます。

ECMAScript 5 Part 1: Reusable CodeECMAScript 5 Part 1: Reusable CodeDeep Cloning an Object
参考にディープコピーとシャローコピーを試してみます。

実際にシャローコピーとディープコピーを確認します。

// deep copy
// http://bytes.com/topic/javascript/answers/715567-deep-cloning-object
function clone_obj(obj) {
    var c = obj instanceof Array ? [] : {};
    for (var i in obj) {
        var prop = obj[i];

        if (typeof prop == 'object') {
           if (prop instanceof Array) {
               c[i] = [];

               for (var j = 0; j < prop.length; j++) {
                   if (typeof prop[j] != 'object') {
                       c[i].push(prop[j]);
                   } else {
                       c[i].push(clone_obj(prop[j]));
                   }
               }
           } else {
               c[i] = clone_obj(prop);
           }
        } else {
           c[i] = prop;
        }
    }

    return c;
}

// shallow copy
// http://www.browserwire.com/?p=33099
function naiveClone (obj) {
  var n = {};
  for (var p in obj) {
    n[p] = obj[p];
  };
  return n;
}

Object.clone = function (o) {
  var n = Object.create(Object.getPrototypeOf(o));
  var props = Object.getOwnPropertyNames(o);
  var pName;
  for (var p in props) {
    pName = props[p];
    Object.defineProperty(n, pName, Object.getOwnPropertyDescriptor(o, pName));
  };
  return n;
};

// shallow copy
var a = { name:'nic', nums:[1,2,3] };
var b = a;
var c = naiveClone(a);
var d = Object.clone(a);

// deep copy
var e = clone_obj(a);

//
a.name = 'yaki';
console.log(a.name);
console.log(b.name);
console.log(c.name);
console.log(d.name);
console.log(e.name);

//
a.nums.shift();
console.log(a.nums);
console.log(b.nums);
console.log(c.nums);
console.log(d.nums);
console.log(e.nums);
コンソールログ
js_obj_copy_1.png
ディープコピーの場合は中身を再帰的に確認し、
空のオブジェクトを作成しプロパティをループしてコピーする必要があります。

[javascript メモ] 継承に関するメモ

A.
プロトタイプに親のインスタンスを指定して継承する方法
子のprototypeに親のインスタンスを渡すことで
プロトタイプチェーンを使い継承します。
// 親のコンストラクタ
var A = function(nickname){
  this.nickname = nickname;
};

// 親のコンストラクタにメソッドの追加
A.prototype.getName = function(){
  return this.nickname;
}

// 子のコンストラクタ
var B = function(nickname, age){
  this.age = age;
  // 親のコンストラクターを呼んでプロパティを引き継ぐ
  A.apply(this, [nickname]);
};

// プロトタイプチェーンを設定
// 親のオブジェクトをプロトタイプにつなげる
B.prototype = new A(); 

// 子にprototypeを追加したい場合 親をつなげてから
// つなげたオブジェクトに対してメソッドを追加する
B.prototype.getAgeName = function(){
  return 'Age:' + this.age  + ' Nick:' + this.nickname;
}

// 親のメソッドと結合する
B.prototype.getName = function(){
  var name = A.prototype.getName.call(this);
  return name + '(' + this.age + ')';
}

// 親のオブジェクトのプロパティが重複するので必要なら削除する
//  delete B.prototype.nickname; 
// コンストラクタを本来あるべきものに戻す
B.prototype.constructor = B; 

// インスタンスの作成
var obj = new B('nic', 20);
var obj2 = new B('yaki', 30);
console.log( obj.getName() );
console.log( obj.getAgeName() );
console.log( obj2.getName() );
console.log( obj2.getAgeName() );

console.dir( obj );
コンソールログは以下のようになります
js_inherit_1.png

結果を図で表してみると以下のようになります
js_inherit_2.png


親のコンストラクタのプロパティをcallし親のコンストラクタのprototypeをコピー
// 親のコンストラクタ
var A = function(nickname){
  this.nickname = nickname;
};

// 親のコンストラクタにメソッドの追加
A.prototype.getName = function(){
  return this.nickname;
}

// 子のコンストラクタ
var B = function(nickname, age){
  this.age = age;
  // 親のコンストラクターを呼んでプロパティを引き継ぐ
  A.apply(this, [nickname]);

  for(var p in A.prototype){
    var proto = this.constructor.prototype;
    if(!proto[p]) proto[p] = A.prototype[p];
  }
};

// メソッドを追加する
B.prototype.getAgeName = function(){
  return 'Age:' + this.age  + ' Nick:' + this.nickname;
};

// 親のメソッドと結合する
B.prototype.getName = function(){
  var name = A.prototype.getName.call(this);
  return name + '(' + this.age + ')';
}

var obj = new B('nic', 20);
var obj2 = new B('yaki', 30);
console.log( obj.getName() );
console.log( obj.getAgeName() );
console.log( obj2.getName() );
console.log( obj2.getAgeName() );

console.dir( obj );
コンソールログは以下のようになります
js_inherit_3.png

結果を図で表してみると以下のようになります
js_inherit_4.png
親のプロパティをnew時にコピーするので、あとから追加しても反映されません。