8939


MonacaクラウドでOnsenUI/AngularJSを使いアプリ制作の勉強中です。

ファイルを扱うAngularJSサービスオブジェクトを作っています。  
フォルダのファイル一覧を取得するメソッドの中でFileSystemAPIを使うため  
コールバック関数を使わなくてはならないのですが、その中でファイル一覧を格納するプロパティ(this.x)を書き換えようとすると以下のようなエラーが出ます(use
strict)。

> Error in Success callbackId: FileXXXXXXXXXX : TypeError: Attempted to assign
> to readonly property.

JavaScriptの仕様のようですが、コールバック関数内でプロパティを変更する  
スマートな回避方法があればお教え頂けると幸いです。

再現する簡略化したコードを追記いたします。  
実際にはエラー発生箇所でディレクトリの中身をプロパティに設定したいと考えております。

[code]

    var obj = function() {
        'use strict';
    
        this.directory = '';
    
        this.changeDirectory = function(directory, returnFunction){
            this.directory = directory; //問題なし
            pstFileSystem.root.getDirectory(directory, {create: true, exclusive: false},
                function(dirEntry){
                    this.directory = directory; //エラー
                    returnFunction(dirEntry);
                }
            );
        };
    };
    
    window.onload = function() {
        'use strict';
    
        console.log('pageinit start!');
        var o = new obj;
        o.changeDirectory("/", function(dirEntry){console.log('directory is "' + o.directory + '"');});
    };
    
[/code]


CC BY-SA 3.0
2015-04-07T08:36:25.410
8861
2019-12-13T18:24:45.993
2015-04-09T04:35:20.870
9031
9031
question
javascript
オブジェクト内で定義したコールバック関数でオブジェクトのプロパティを変更する方法


`this` が指すものはその関数が実行されるタイミングで決定するので、コールバック設定時の `this` と一致することを期待してはいけません。

[JavaScript の this キーワードは何を指すのか - コールバック関数内では this を使ってはいけない - vivid
memo](http://d.hatena.ne.jp/vividcode/20110106/1294336737)

この場合 `this` を他の変数に保存しておいて、コールバック関数内ではそれを使うとよいでしょう。

[code]

    this.changeDirectory = function(directory, returnFunction){
        var self = this; //この時点でのthisを保存
        pstFileSystem.root.getDirectory(directory, {create: true, exclusive: false},
            function(dirEntry){
                self.directory = directory;
                returnFunction(dirEntry);
            }
        );
    };
    
[/code]

例:

[code]

    var Hoge = function() {
      this.prop = "OK";
      this.run = function() {
        var self = this;
        setTimeout(function() {
          $('body').html(
            'this: ' + this.prop + "<br/>" +
            'self: ' + self.prop
          );
        }, 0);
      };
    };
    
    var o = new Hoge();
    o.run();
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
[/code]

なお、 `this` を保存するタイミングは `changeDirectory` の外側、 `obj` の中でも構いません。


CC BY-SA 4.0
2015-04-09T05:44:29.930
8935
2019-12-13T18:24:45.993
2019-12-13T18:24:45.993
32986
8000
8861
answer


外側のスコープの `this` にアクセスする方法は、方法は3つあります。

ひとつめ。ローカル変数に外側のスコープの `this` を保管して、それを使う。

[code]

    var self = this;
    ...
    function(dirEntry){
      self.directory = directory;
      returnFunction(dirEntry);
    }
    
[/code]

ふたつめ。コールバック関数の `this` に外側のスコープの `this` を
[bind](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)
する。

[code]

    function(dirEntry){
      this.directory = directory;
      returnFunction(dirEntry);
    }.bind(this)
    
[/code]

このようにすると、コールバック関数の `this` は外側のスコープの `this` と同じオブジェクトになります。(IE9以降で動作します)

みっつめ。 ES2015 (ES6) で追加された ArrowFunction を使う。

[code]

    (dirEntry) => {
      this.directory = directory;
      returnFunction(dirEntry);
    }
    
[/code]

ArrowFunction では、`this` は外側のスコープのものがそのまま使われます。(Chrome, Edge, Firefox, Opera
で動作します)


CC BY-SA 3.0
2015-04-09T06:31:11.387
8939
2015-12-22T07:54:03.743
2015-12-22T07:54:03.743
4809
4809
8861
answer