<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Vuex - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Vuex - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 19:55:41 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/vuex/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 尤雨溪在 Vuex 源码中是怎么处理 this 指向丢失的 ]]>
                </title>
                <description>
                    <![CDATA[ 最近有小伙伴看我的 Vuex源码 [http://mp.weixin.qq.com/s?__biz=MzA5MjQwMzQyNw==&mid=2650744584&idx=1&sn=b14f8a762f132adcf0f7e3e075ee2ded&chksm=88662484bf11ad922ed27d45873af838298949eea381545e82a511cabf0c6fc6876a8370c6fb&scene=21#wechat_redirect] 文章，提到有一处this指向有点看不懂（好不容易终于有人看我的源码文章了，感动得要流泪了^_^）。于是我写篇文章答疑解惑，简单再说说 this 指向和尤大在 Vuex 源码中是怎么处理 this 指向丢失的。 2. 对象中的this指向 var person = {   name: '若川',   say: function(text){     console.log(this.name + ', ' + text);   } } console.log(person.name); console.log(person.sa ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/this-in-vuex/</link>
                <guid isPermaLink="false">61349c24abc6f30645ecce8c</guid>
                
                    <category>
                        <![CDATA[ Vuex ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 若川 ]]>
                </dc:creator>
                <pubDate>Sun, 05 Sep 2021 10:35:08 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/09/glenn-carstens-peters-npxXWgQ33ZQ-unsplash-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>最近有小伙伴看我的 <a href="http://mp.weixin.qq.com/s?__biz=MzA5MjQwMzQyNw==&amp;mid=2650744584&amp;idx=1&amp;sn=b14f8a762f132adcf0f7e3e075ee2ded&amp;chksm=88662484bf11ad922ed27d45873af838298949eea381545e82a511cabf0c6fc6876a8370c6fb&amp;scene=21#wechat_redirect" rel="noopener noreferrer">Vuex源码</a>文章，提到有一处<code>this</code>指向有点看不懂（好不容易终于有人看我的源码文章了，感动得要流泪了<code>^_^</code>）。于是我写篇文章答疑解惑，简单再说说 <code>this</code> 指向和<code>尤大在 Vuex 源码中</code>是怎么处理 <code>this</code> 指向丢失的。</p><h2 id="2-this-">2. 对象中的this指向</h2><pre><code class="language-js">var person = {
  name: '若川',
  say: function(text){
    console.log(this.name + ', ' + text);
  }
}
console.log(person.name);
console.log(person.say('在写文章')); // 若川, 在写文章
var say = person.say;
say('在写文章'); // 这里的this指向就丢失了，指向window了。(非严格模式)
</code></pre><h2 id="3-this-">3. 类中的this指向</h2><h3 id="3-1-es5">3.1 ES5</h3><pre><code class="language-js">// ES5
var Person = function(){
  this.name = '若川';
}
Person.prototype.say = function(text){
  console.log(this.name + ', ' + text);
}
var person = new Person();
console.log(person.name); // 若川
console.log(person.say('在写文章'));
var say = person.say;
say('在写文章'); // 这里的this指向就丢失了，指向 window 了。
</code></pre><h3 id="3-2-es6">3.2 ES6</h3><pre><code class="language-js">// ES6
class Person{
  construcor(name = '若川'){
     this.name = name;
  }
  say(text){
    console.log(`${this.name}, ${text}`);
  }
}
const person = new Person();
person.say('在写文章')
// 解构
const { say } = person;
say('在写文章'); // 报错 this ，因为ES6 默认启用严格模式，严格模式下指向 undefined
</code></pre><h2 id="4-vuex-">4. 尤大在Vuex源码中是怎么处理的</h2><p>先看代码：</p><pre><code class="language-js">class Store{
  constructor(options = {}){
     this._actions = Object.create(null);
  // bind commit and dispatch to self
      // 给自己 绑定 commit 和 dispatch
      const store = this
      const { dispatch, commit } = this
      // 为何要这样绑定 ?
      // 说明调用commit和dispach 的 this 不一定是 store 实例
      // 这是确保这两个函数里的this是store实例
      this.dispatch = function boundDispatch (type, payload) {
        return dispatch.call(store, type, payload)
      }
      this.commit = function boundCommit (type, payload, options) {
        return commit.call(store, type, payload, options)
      }
  }
  dispatch(){
     console.log('dispatch', this);
  }
  commit(){
     console.log('commit', this);
  }
}
const store = new Store();
store.dispatch(); // 输出结果 this 是什么呢？

const { dispatch, commit } = store;
dispatch(); // 输出结果 this 是什么呢？
commit();  // 输出结果 this 是什么呢？
</code></pre><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2021/09/store-dispatch-example.jpg" class="kg-image" alt="store-dispatch-example" width="600" height="400" loading="lazy"></figure><p><strong>结论</strong>：非常巧妙的用了<code>call</code>把<code>dispatch</code>和<code>commit</code>函数的<code>this</code>指向强制绑定到<code>store</code>实例对象上。如果不这么绑定就报错了。</p><h3 id="4-1-actions-store">4.1 actions 解构 store</h3><p>其实<code>Vuex</code>源码里就有上面解构<code>const { dispatch, commit } = store;</code>的写法。想想我们平时是如何写<code>actions</code>的。<code>actions</code>中自定义函数的第一个参数其实就是 <code>store</code> 实例。</p><p>这时我们翻看下<code>actions文档</code>：<code>https://vuex.vuejs.org/zh/guide/actions.html</code></p><pre><code class="language-js">const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})
</code></pre><p>也可以用解构赋值的写法。</p><pre><code class="language-js">actions: {
  increment ({ commit }) {
    commit('increment')
  }
}
</code></pre><p>有了<code>Vuex</code>源码构造函数里的<code>call</code>绑定，这样<code>this</code>指向就被修正啦~<strong>不得不说祖师爷就是厉害</strong>。这一招，大家可以免费学走~</p><p>接着我们带着问题，为啥上文中的<code>context</code>就是<code>store</code>实例，有<code>dispatch</code>、<code>commit</code>这些方法呢。继续往下看。</p><h3 id="4-2-actions-store-">4.2 为什么 actions 对象里的自定义函数 第一个参数就是 store 实例。</h3><p>以下是简单源码，有缩减，感兴趣的可以看我的<a href="https://lxchuan12.gitee.io/vuex" rel="noopener noreferrer">Vuex源码文章</a>。</p><pre><code class="language-js">class Store{
 construcor(){
    // 初始化 根模块
    // 并且也递归的注册所有子模块
    // 并且收集所有模块的 getters 放在 this._wrappedGetters 里面
    installModule(this, state, [], this._modules.root)
 }
}
</code></pre><p>接着我们看<code>installModule</code>函数中的遍历注册 <code>actions</code> 实现</p><pre><code class="language-js">function installModule (store, rootState, path, module, hot) {
    // 省略若干代码
    // 循环遍历注册 action
    module.forEachAction((action, key) =&gt; {
      const type = action.root ? key : namespace + key
      const handler = action.handler || action
      registerAction(store, type, handler, local)
    })
}
</code></pre><p>接着看注册 <code>actions</code> 函数实现 <code>registerAction</code></p><pre><code class="language-js">/**
* 注册 mutation
* @param {Object} store 对象
* @param {String} type 类型
* @param {Function} handler 用户自定义的函数
* @param {Object} local local 对象
*/
function registerAction (store, type, handler, local) {
  const entry = store._actions[type] || (store._actions[type] = [])
  // payload 是actions函数的第二个参数
  entry.push(function wrappedActionHandler (payload) {
    /**
     * 也就是为什么用户定义的actions中的函数第一个参数有
     *  { dispatch, commit, getters, state, rootGetters, rootState } 的原因
     * actions: {
     *    checkout ({ commit, state }, products) {
     *        console.log(commit, state);
     *    }
     * }
     */
    let res = handler.call(store, {
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }, payload)
    // 源码有删减
}
</code></pre><p>比较容易发现调用顺序是 <code>new Store() =&gt; installModule(this) =&gt; registerAction(store) =&gt; let res = handler.call(store)</code>。</p><p>其中<code>handler</code> 就是 用户自定义的函数，也就是对应上文的例子<code>increment</code>函数。<code>store</code>实例对象一路往下传递，到<code>handler</code>执行时，也是用了<code>call</code>函数，强制绑定了第一个参数是<code>store</code>实例对象。</p><pre><code class="language-js">actions: {
  increment ({ commit }) {
    commit('increment')
  }
}
</code></pre><p>这也就是为什么 <code>actions</code> 对象中的自定义函数的第一个参数是 <code>store</code> 对象实例了。</p><p>好啦，文章到这里就基本写完啦~相对简短一些。应该也比较好理解。</p><h2 id="5-this-">5. 最后再总结下 this 指向</h2><p>摘抄下<a href="https://chinese.freecodecamp.org/news/javascript-this/">面试官问：this 指向</a>文章结尾。</p><p>如果要判断一个运行中函数的 <code>this</code> 绑定， 就需要找到这个函数的直接调用位置。 找到之后 就可以顺序应用下面这四条规则来判断 <code>this</code> 的绑定对象。<br></p><ol><li><code>new</code> 调用：绑定到新创建的对象，注意：显示<code>return</code>函数或对象，返回值不是新创建的对象，而是显式返回的函数或对象。<br></li><li><code>call</code> 或者 <code>apply</code>（ 或者 <code>bind</code>） 调用：严格模式下，绑定到指定的第一个参数。非严格模式下，<code>null</code>和<code>undefined</code>，指向全局对象（浏览器中是<code>window</code>），其余值指向被<code>new Object()</code>包装的对象。<br></li><li>对象上的函数调用：绑定到那个对象。<br></li><li>普通函数调用： 在严格模式下绑定到 <code>undefined</code>，否则绑定到全局对象。</li></ol><p><code>ES6</code> 中的箭头函数：不会使用上文的四条标准的绑定规则， 而是根据当前的词法作用域来决定<code>this</code>， 具体来说， 箭头函数会继承外层函数，调用的 this 绑定（ 无论 this 绑定到什么），没有外层函数，则是绑定到全局对象（浏览器中是<code>window</code>）。 这其实和 <code>ES6</code> 之前代码中的 <code>self = this</code> 机制一样。</p><p>欢迎在<a href="https://lxchuan12.gitee.io/">我的博客</a>阅读更多文章。</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
