• JavaScript ASI 机制详解 at May 05, 2016

    平时不用分号,只有在特定情况下,基本上是[,(,+,-,/开头的地方(这是在犀牛书里面看到的,实际工作中也就是那么几种情况下). 比如:

    const FOO = "FOO"
    
    ;[1].forEach(item => console.log(item))
    
    ;(function() {
       return console.log("bar")
    }())
    
    for (let i = 0; i < 2; i+=1) console.log(i)
    
  • #26 楼 @darkbaby123 有没有什么工作只专注于写前端接口和算法的?感觉前端的业务真的是太难写了。不仅要对接后端 API,ajax 还是 fetch,各种异步处理的标准,数据结构处理,还要写组件,之下还有事件、动效,语义化 HTML,CSS 模块,还有 UI。当然还有自动化等等。一直没玩过 emberjS,但是不知道为什么,越来越对它有好感。

  • const pattern = /^(?:\s*(<[\w\W]+>)[^>]|#([\w-]))$/
    
    /^ (?:  \s* (  <[\w\W]+>   )  [^>] | # (  [\w-]  )   )$/
    
    • // 区间为正则表达式
    • ^()$ 匹配从开头至结尾所有的内容
    • (?:) 该匹配不记结果,就是你用pattern.exec("some string"),就算匹配成功,但不反回匹配结果
    • \s* 匹配0至多个空白符号
    • (<[\w\W]+>) 匹配<whatever>, 即:尖括号之内至少有一个[A-Za-z0-9_][^A-Za-z0-9_]字符,并且返回结果
    • [^>]>
    • |
    • # 匹配#符号
    • ([\w-]) 匹配`[A-Za-z0-9_]-,并且返回结果。

    大致能匹配到的字符串是:

    "    <a   >#" 
    "   <b      >-"
    "   <c    >0"
    "<d>z"
    
  • const FOO = "FOO"
    const BAR = "BAR"
    console.log(`${FOO}${BAR}`) // => "FOOBAR"
    
  • 先收藏了。感谢。

  • #12 楼 @darkbaby123 #8 楼 @nightire

    刚才稍微改了下我的方案,似乎也可以。假设,截获所有REQUEST|SUCCESS|FAILURE的动作,然后根据前缀使用制定的reducer。见上面。

  • #8 楼 @nightire 刚才仔细再看了一遍,你的这种 raw 方式就是我想要的。不过我也是把 fetch 再用 promise 封装了一层。只是少了个全局处理的 action。感谢。

  • #8 楼 @nightire Promise.all是个好东西。:D 这样基本可以解决建立一个fetchQueue的队列了,然后一个个检查fetch结果。

  • #8 楼 @nightire 好的,太感谢了。太感动了。好人好报。

  • 这是我想到的一种方案,但不知道业务膨胀之后会否比较难控制。

    reducers/index.js

    import { assign } from "../utils"
    import login from "./auth/login"
    // ...
    
    const initState = {
        isFetching: false,
        isAuthenticated: !!cookie.parse(document.cookie)["_at_"]
    }
    
    const reducerMap = {
        "LOGIN": login
        // ...
    }
    
    export default function app(state = initState, action) {
        const type = action.type
        let tmpState
    
        // when `"LOGIN_REQUEST"`
        if (/REQUEST/.test(type)) {
            tmpState = assign({}, state, {
                isFetching: true
            })
        }
    
        // when `"LOGIN_SUCCESS"` or `"LOGIN_FAILURE"`
        if (/SUCCESS|FAILURE/.test(type)) {
            tmpState = assign({}, state, {
                isFetching: false
            })
        }
    
        for (let prop in reducerMap) {
    
            // `"LOGIN_REQUEST".indexOf("LOGIN")`
            // `"LOGIN_SUCCESS".indexOf("LOGIN")`
            // `"LOGIN_FAILURE".indexOf("LOGIN")`
            if (type.indexOf(prop) > -1) {
    
                // return reducerMap["LOGIN"](tmpState, action)
                return reducerMap[prop](tmpState, action)
            }
        }
    
        // default
        return state
    }
    
    

    actions/auth/login.js

    import {
        LOGIN_SUCCESS,
        LOGIN_FAILURE
    } from "../../constants/actionTypes"
    import { assign } from "../../utils"
    
    export default function login(state, { type, payload }) {
        if (type === LOGIN_SUCCESS) {
            return assign({}, state, {
                isAuthenticated: true,
                message: payload.message
            })
        }
    
        if (type === LOGIN_FAILURE) {
            return assign({}, state, {
                message: payload.message
            })
        }
    }
    

    constants/actionTypes.js

    // LOGIN
    export const LOGIN_REQUEST = "LOGIN_REQUEST"
    export const LOGIN_SUCCESS = "LOGIN_SUCCESS"
    export const LOGIN_FAILURE = "LOGIN_FAILURE"
    
  • #2 楼 @nightire 没怎么看懂。 :(

  • #1 楼 @small_fish__ 感谢,,关于项目中的 reducer 一般分为数据相关和 UI 相关,我们的经验是数据相关以资源命名放在顶层,所以 UI 状态以 UI namespce 放在顶层 . 可以给具体一些的例子吗?最好可以看代码,谢谢。

  • 定制的几个 pixel 艺术主题。terminal 主题。

  • #4 楼 @huacnlee 可以改的,各种主题。哈哈 太 cool 了。 #1 楼 @zhaopei 你 google 搜一下,能下载的到 (关键字包含:cathode download,第一页就有)。

  • 刚上手 redux,之前写了一个多月 reflux,真想吐槽,去你妹的!

    之前 angular 刚感觉熟练了,结果被迫丢掉 (算一门技能)。开始写 react,真是几乎又新学了一门技能(对于年龄大的人而言,真是要命),由于业务原因,原先的项目是 reflux 做的,我接手以后 (也是我第一次用 react),辛辛苦苦花了几天强记 reflux 的 api 文档,然后写了个把月,算是有些理解了,妈逼的又要叫我改写 redux,然后苦逼的 redux(真的想说给傻逼用的),又是看文档,搜教程 (对于年纪大的我而言真是心脏受不了,都想改行了)。学 redux 一来就是一个下马威,全部 es6(先不说一堆需要 polyfill 的特性,他妈的搞得我好郁闷啊,这个方法到底用不用啊?!!!),然后一堆 (action,actionTypes,reducers(如何 combineReducer 或不 combine, normalizr 数据,到现在我对于设计全局 store 的 state 还是一头雾水),然后 react 又是 class 写法,又是 staeless 写法(完全搞不明白为什么又是这样定义一个 react component,怎么又是那样定义一个 component),然后妈逼的蛋疼的 router 处理,先来个 react-router,结果处理跳转各种其他需求,又来了个 recat-router-redux(妈逼的)。花了好多时间终于能把 counter 这个小小的简单应用写出来了。然后又是 async 处理,redux middleware,还有 store 映射到 react component(所谓的 map(State||Dispatch)ToProps),怪哉,他妈的怎么有些情况在 dispatch 里面要访问 stateProps,还要多写个 mergeProps, 最后把 state 和 dispatch handlers 绑定到 component。你妈的,想到这里就想骂人了。跟什么 P 的风呀,怀疑 angular 的性能,臃肿 (其实一般业务哪里有那么高的需求),不行就 jquery 吧,再不行,尝试 vanilla javascript 吧,反正现在的浏览器对于兼容性都基本趋向一致了,至于模块管理,什么 browserify, webpack(code-splitting, chunk) 全都是负担,我觉得 requireJS 就很靠谱了,毕竟咱们现在还是小前端。踏踏实实能把代码写的漂漂亮亮,不要每次动不动就推到重来就行 (基本上公司换人都不能接手别人的项目,还有蛋疼的 coffeescript)。

  • @huacnlee @lgn21st 感谢两位元老 跟他说了,我说这是个大学问,暂时先自己看着感觉调了。

  • 这些是我剥离出来的核心的几百行代码。

    (function(window, document, undefined) {
    
    var
        jqLite,           // delay binding since jQuery could be loaded after us.
        jQuery,           // delay binding
    
        /** @name angular */
        angular           = window.angular || (window.angular = {}),
        angularModule,
        uid               = 0;
    
    function noop() {}
    noop.$inject = [];
    
    function identity($) {return $;}
    identity.$inject = [];
    
    var jq = function() {
      if (isDefined(jq.name_)) return jq.name_;
      var el;
      var i, ii = ngAttrPrefixes.length, prefix, name;
      for (i = 0; i < ii; ++i) {
        prefix = ngAttrPrefixes[i];
        if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
          name = el.getAttribute(prefix + 'jq');
          break;
        }
      }
    
      return (jq.name_ = name);
    };
    
    var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
    
    function getNgAttribute(element, ngAttr) {
      var attr, i, ii = ngAttrPrefixes.length;
      for (i = 0; i < ii; ++i) {
        attr = ngAttrPrefixes[i] + ngAttr;
        if (isString(attr = element.getAttribute(attr))) {
          return attr;
        }
      }
      return null;
    }
    
    function angularInit(element, bootstrap) {
      var appElement,
          module,
          config = {};
    
      // The element `element` has priority over any other element
      forEach(ngAttrPrefixes, function(prefix) {
        var name = prefix + 'app';
    
        if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
          appElement = element;
          module = element.getAttribute(name);
        }
      });
      forEach(ngAttrPrefixes, function(prefix) {
        var name = prefix + 'app';
        var candidate;
    
        if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
          appElement = candidate;
          module = candidate.getAttribute(name);
        }
      });
      if (appElement) {
        config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
        bootstrap(appElement, module ? [module] : [], config);
      }
    }
    
    function bootstrap(element, modules, config) {
      if (!isObject(config)) config = {};
      var defaultConfig = {
        strictDi: false
      };
      config = extend(defaultConfig, config);
      var doBootstrap = function() {
        element = jqLite(element);
    
        if (element.injector()) {
          var tag = (element[0] === document) ? 'document' : startingTag(element);
          //Encode angle brackets to prevent input from being sanitized to empty string #8683
          throw ngMinErr(
              'btstrpd',
              "App Already Bootstrapped with this Element '{0}'",
              tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
        }
    
        modules = modules || [];
        modules.unshift(['$provide', function($provide) {
          $provide.value('$rootElement', element);
        }]);
    
        if (config.debugInfoEnabled) {
          // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
          modules.push(['$compileProvider', function($compileProvider) {
            $compileProvider.debugInfoEnabled(true);
          }]);
        }
    
        modules.unshift('ng');
        var injector = createInjector(modules, config.strictDi);
        injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
           function bootstrapApply(scope, element, compile, injector) {
            scope.$apply(function() {
              element.data('$injector', injector);
              compile(element)(scope);
            });
          }]
        );
        return injector;
      };
    
      var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
      var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
    
      if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
        config.debugInfoEnabled = true;
        window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
      }
    
      if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
        return doBootstrap();
      }
    
      window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
      angular.resumeBootstrap = function(extraModules) {
        forEach(extraModules, function(module) {
          modules.push(module);
        });
        return doBootstrap();
      };
    
      if (isFunction(angular.resumeDeferredBootstrap)) {
        angular.resumeDeferredBootstrap();
      }
    }
    
    function bindJQuery() {
      var originalCleanData;
    
      if (bindJQueryFired) {
        return;
      }
    
      // bind to jQuery if present;
      var jqName = jq();
      jQuery = window.jQuery; // use default jQuery.
      if (isDefined(jqName)) { // `ngJq` present
        jQuery = jqName === null ? undefined : window[jqName]; // if empty; use jqLite. if not empty, use jQuery specified by `ngJq`.
      }
    
      // Use jQuery if it exists with proper functionality, otherwise default to us.
      // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
      // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
      // versions. It will not work for sure with jQuery <1.7, though.
      if (jQuery && jQuery.fn.on) {
        jqLite = jQuery;
        extend(jQuery.fn, {
          scope: JQLitePrototype.scope,
          isolateScope: JQLitePrototype.isolateScope,
          controller: JQLitePrototype.controller,
          injector: JQLitePrototype.injector,
          inheritedData: JQLitePrototype.inheritedData
        });
    
        // All nodes removed from the DOM via various jQuery APIs like .remove()
        // are passed through jQuery.cleanData. Monkey-patch this method to fire
        // the $destroy event on all removed nodes.
        originalCleanData = jQuery.cleanData;
        jQuery.cleanData = function(elems) {
          var events;
          if (!skipDestroyOnNextJQueryCleanData) {
            for (var i = 0, elem; (elem = elems[i]) != null; i++) {
              events = jQuery._data(elem, "events");
              if (events && events.$destroy) {
                jQuery(elem).triggerHandler('$destroy');
              }
            }
          } else {
            skipDestroyOnNextJQueryCleanData = false;
          }
          originalCleanData(elems);
        };
      } else {
        jqLite = JQLite;
      }
    
      angular.element = jqLite;
    
      // Prevent double-proxying.
      bindJQueryFired = true;
    }
    
    function setupModuleLoader(window) {
    
      var $injectorMinErr = minErr('$injector');
      var ngMinErr = minErr('ng');
    
      function ensure(obj, name, factory) {
        return obj[name] || (obj[name] = factory());
      }
    
      var angular = ensure(window, 'angular', Object);
    
      // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
      angular.$$minErr = angular.$$minErr || minErr;
    
      return ensure(angular, 'module', function() {
        /** @type {Object.<string, angular.Module>} */
        var modules = {};
    
        return function module(name, requires, configFn) {
          var assertNotHasOwnProperty = function(name, context) {
            if (name === 'hasOwnProperty') {
              throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
            }
          };
    
          assertNotHasOwnProperty(name, 'module');
          if (requires && modules.hasOwnProperty(name)) {
            modules[name] = null;
          }
          return ensure(modules, name, function() {
            if (!requires) {
              throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
                 "the module name or forgot to load it. If registering a module ensure that you " +
                 "specify the dependencies as the second argument.", name);
            }
    
            /** @type {!Array.<Array.<*>>} */
            var invokeQueue = [];
    
            /** @type {!Array.<Function>} */
            var configBlocks = [];
    
            /** @type {!Array.<Function>} */
            var runBlocks = [];
    
            var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
    
            /** @type {angular.Module} */
            var moduleInstance = {
              // Private state
              _invokeQueue: invokeQueue,
              _configBlocks: configBlocks,
              _runBlocks: runBlocks,
    
              requires: requires,
    
              name: name,
    
              provider: invokeLaterAndSetModuleName('$provide', 'provider'),
    
              factory: invokeLaterAndSetModuleName('$provide', 'factory'),
    
              service: invokeLaterAndSetModuleName('$provide', 'service'),
    
              value: invokeLater('$provide', 'value'),
    
              constant: invokeLater('$provide', 'constant', 'unshift'),
    
              decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
    
              animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
    
              filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
    
              controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
    
              directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
    
              config: config,
    
              run: function(block) {
                runBlocks.push(block);
                return this;
              }
            };
    
            if (configFn) {
              config(configFn);
            }
    
            return moduleInstance;
    
            function invokeLater(provider, method, insertMethod, queue) {
              if (!queue) queue = invokeQueue;
              return function() {
                queue[insertMethod || 'push']([provider, method, arguments]);
                return moduleInstance;
              };
            }
    
            function invokeLaterAndSetModuleName(provider, method) {
              return function(recipeName, factoryFunction) {
                if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
                invokeQueue.push([provider, method, arguments]);
                return moduleInstance;
              };
            }
          });
        };
      });
    
    }
    
    function publishExternalAPI(angular) {
    
      angularModule = setupModuleLoader(window);
    
      angularModule('ng', ['ngLocale'], ['$provide',
        function ngModule($provide) {
          $provide.provider({
            $$sanitizeUri: $$SanitizeUriProvider
          });
          $provide.provider('$compile', $CompileProvider).
            directive({
                a: htmlAnchorDirective,
                // all other directives ....
            }).
            directive({
              ngInclude: ngIncludeFillContentDirective
            }).
            directive(ngAttributeAliasDirectives).
            directive(ngEventDirectives);
          $provide.provider({
            $browser: $BrowserProvider,
            $cacheFactory: $CacheFactoryProvider,
            $controller: $ControllerProvider,
            $document: $DocumentProvider,
            $filter: $FilterProvider,
            $interpolate: $InterpolateProvider,
            $interval: $IntervalProvider,
            $http: $HttpProvider,
            $location: $LocationProvider,
            $log: $LogProvider,
            $parse: $ParseProvider,
            $rootScope: $RootScopeProvider,
            $q: $QProvider,
            $$q: $$QProvider,
            $templateCache: $TemplateCacheProvider,
            $timeout: $TimeoutProvider,
            $window: $WindowProvider,
            $$jqLite: $$jqLiteProvider,
            // and other providers
          });
        }
      ]);
    }
    
    function createInjector(modulesToLoad, strictDi) {
      strictDi = (strictDi === true);
      var INSTANTIATING = {},
          providerSuffix = 'Provider',
          path = [],
          loadedModules = new HashMap([], true),
          providerCache = {
            $provide: {
                provider: supportObject(provider),
                factory: supportObject(factory),
                service: supportObject(service),
                value: supportObject(value),
                constant: supportObject(constant),
                decorator: decorator
              }
          },
          providerInjector = (providerCache.$injector =
              createInternalInjector(providerCache, function(serviceName, caller) {
                if (angular.isString(caller)) {
                  path.push(caller);
                }
                throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
              })),
          instanceCache = {},
          instanceInjector = (instanceCache.$injector =
              createInternalInjector(instanceCache, function(serviceName, caller) {
                var provider = providerInjector.get(serviceName + providerSuffix, caller);
                return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
              }));
    
    
      forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
    
      return instanceInjector;
    
      ////////////////////////////////////
      // $provider
      ////////////////////////////////////
    
      function supportObject(delegate) {
        return function(key, value) {
          if (isObject(key)) {
            forEach(key, reverseParams(delegate));
          } else {
            return delegate(key, value);
          }
        };
      }
    
      function provider(name, provider_) {
        assertNotHasOwnProperty(name, 'service');
        if (isFunction(provider_) || isArray(provider_)) {
          provider_ = providerInjector.instantiate(provider_);
        }
        if (!provider_.$get) {
          throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
        }
        return providerCache[name + providerSuffix] = provider_;
      }
    
      function enforceReturnValue(name, factory) {
        return function enforcedReturnValue() {
          var result = instanceInjector.invoke(factory, this);
          if (isUndefined(result)) {
            throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
          }
          return result;
        };
      }
    
      function factory(name, factoryFn, enforce) {
        return provider(name, {
          $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
        });
      }
    
      function service(name, constructor) {
        return factory(name, ['$injector', function($injector) {
          return $injector.instantiate(constructor);
        }]);
      }
    
      function value(name, val) { return factory(name, valueFn(val), false); }
    
      function constant(name, value) {
        assertNotHasOwnProperty(name, 'constant');
        providerCache[name] = value;
        instanceCache[name] = value;
      }
    
      function decorator(serviceName, decorFn) {
        var origProvider = providerInjector.get(serviceName + providerSuffix),
            orig$get = origProvider.$get;
    
        origProvider.$get = function() {
          var origInstance = instanceInjector.invoke(orig$get, origProvider);
          return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
        };
      }
    
      ////////////////////////////////////
      // Module Loading
      ////////////////////////////////////
      function loadModules(modulesToLoad) {
        assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
        var runBlocks = [], moduleFn;
        forEach(modulesToLoad, function(module) {
          if (loadedModules.get(module)) return;
          loadedModules.put(module, true);
    
          function runInvokeQueue(queue) {
            var i, ii;
            for (i = 0, ii = queue.length; i < ii; i++) {
              var invokeArgs = queue[i],
                  provider = providerInjector.get(invokeArgs[0]);
    
              provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
            }
          }
    
          try {
            if (isString(module)) {
              moduleFn = angularModule(module);
              runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
              runInvokeQueue(moduleFn._invokeQueue);
              runInvokeQueue(moduleFn._configBlocks);
            } else if (isFunction(module)) {
                runBlocks.push(providerInjector.invoke(module));
            } else if (isArray(module)) {
                runBlocks.push(providerInjector.invoke(module));
            } else {
              assertArgFn(module, 'module');
            }
          } catch (e) {
            if (isArray(module)) {
              module = module[module.length - 1];
            }
            if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
              // Safari & FF's stack traces don't contain error.message content
              // unlike those of Chrome and IE
              // So if stack doesn't contain message, we create a new string that contains both.
              // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
              /* jshint -W022 */
              e = e.message + '\n' + e.stack;
            }
            throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
                      module, e.stack || e.message || e);
          }
        });
        return runBlocks;
      }
    
      ////////////////////////////////////
      // internal Injector
      ////////////////////////////////////
    
      function createInternalInjector(cache, factory) {
    
        function getService(serviceName, caller) {
          if (cache.hasOwnProperty(serviceName)) {
            if (cache[serviceName] === INSTANTIATING) {
              throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
                        serviceName + ' <- ' + path.join(' <- '));
            }
            return cache[serviceName];
          } else {
            try {
              path.unshift(serviceName);
              cache[serviceName] = INSTANTIATING;
              return cache[serviceName] = factory(serviceName, caller);
            } catch (err) {
              if (cache[serviceName] === INSTANTIATING) {
                delete cache[serviceName];
              }
              throw err;
            } finally {
              path.shift();
            }
          }
        }
    
        function invoke(fn, self, locals, serviceName) {
          if (typeof locals === 'string') {
            serviceName = locals;
            locals = null;
          }
    
          var args = [],
              $inject = createInjector.$$annotate(fn, strictDi, serviceName),
              length, i,
              key;
    
          for (i = 0, length = $inject.length; i < length; i++) {
            key = $inject[i];
            if (typeof key !== 'string') {
              throw $injectorMinErr('itkn',
                      'Incorrect injection token! Expected service name as string, got {0}', key);
            }
            args.push(
              locals && locals.hasOwnProperty(key)
              ? locals[key]
              : getService(key, serviceName)
            );
          }
          if (isArray(fn)) {
            fn = fn[length];
          }
    
          // http://jsperf.com/angularjs-invoke-apply-vs-switch
          // #5388
          return fn.apply(self, args);
        }
    
        function instantiate(Type, locals, serviceName) {
          // Check if Type is annotated and use just the given function at n-1 as parameter
          // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
          // Object creation: http://jsperf.com/create-constructor/2
          var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
          var returnedValue = invoke(Type, instance, locals, serviceName);
    
          return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
        }
    
        return {
          invoke: invoke,
          instantiate: instantiate,
          get: getService,
          annotate: createInjector.$$annotate,
          has: function(name) {
            return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
          }
        };
      }
    }
    
    createInjector.$$annotate = annotate;
    
    if (window.angular.bootstrap) {
      console.log('WARNING: Tried to load angular more than once.');
      return;
    }
    
    //try to bind to jquery now so that one can write jqLite(document).ready()
    //but we will rebind on bootstrap again.
    bindJQuery();
    
    publishExternalAPI(angular);
    
    jqLite(document).ready(function() {
    angularInit(document, bootstrap);
    });
    
    })(window, document);
    
    !window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
    
    
    
  • #52 楼 @camel 嗯,谢谢,这个人的那两篇文章我之前也搜到过了,看他写的还是蛮复杂的,如果简单的任务直接写在scripts属性里面似乎更方便,但是如果复杂的,我觉得还是直接写在Makefile里面更好 (又要学写 Makefile 了)。

    我再研究研究,不过方向基本定下来朝这个方向去了。

  • #50 楼 @camel 我想问下,如果单纯用 browserify(或者 webpack), 能否完全去掉用(grunt|gulp)scss,image,font等其他资源的编译,复制,压缩等,以及处理发布应用的任务? 我研究了几天,除了简单的玩起来,但是对于处理大型复杂的task`还是没有太多想法。能否指点一下。谢谢

  • #44 楼 @camel 谢谢,我回头研究下

  • #42 楼 @camel 谢谢,我打算用 commonjs 和 es6 的规范来写两个 sample 先,至少先熟悉了他们的规范。 至于 nodejs api 那块,单纯对于 expressjs 这个框架而言我觉得上手应该 3-5 天,但是要熟悉估计得至少练习 1-2 个月以上。

    我也老早就想去掉 bower 了 (实在难用),grunt,gulp,是不是用 webpack 和 component 就能替代了?

  • #37 楼 @camel bower 实在是挺蛋疼的,grunt,gulp 在我看来几乎就是用于自动化发布的功能。

    webpack 和 component 没研究过 (随便看了看),我也是希望能有更合适的工具,能够更加细粒度的 component 化组件。

  • #16 楼 @camel 想写 react,但是公司不给机会,自己之前练手过,现在全忘了。还在弄 angular。对了,现在 commonjs 和 es6 已经是事实上的前端开发标准了吗?我还在弄 amd。是不是过时了啊?

    另外一个合格的前端是不是还要会写 nodejs 的 api?

  • #4 楼 @flemon 但是你现在的 PR 还是有点儿问题。晚点儿,我在 github 上回你好了。谢谢

  • #4 楼 @flemon 谢谢,是这样,因为define()内部require()也是异步的,我以为 requirejs 的工作原理是不管如何,只要使用这两个方法,都是必须等待他们 deps 执行完之后才会执行 callback 的。其实不然。

  • #1 楼 @luikore 我也试过了,还是那个错误。

  • #31 楼 @stevenlii 那是以前找的一个colorscheme,现在我也不知道是哪个了,都几年前的帖子了哦。

  • #32 楼 @maloneguo 是这样的,即是你把 terminal 的 window size 设置成最大也没有办法,但 iterm2 似乎可以把你的 window 固定在顶部。不过我一般control+command+f 全屏模式,然后用screen来当作多屏的。