.= 'tag.htm'; break; case 'flag': $pre .= $default_pre .= 'flag.htm'; break; case 'my': $pre .= $default_pre .= 'my.htm'; break; case 'my_password': $pre .= $default_pre .= 'my_password.htm'; break; case 'my_bind': $pre .= $default_pre .= 'my_bind.htm'; break; case 'my_avatar': $pre .= $default_pre .= 'my_avatar.htm'; break; case 'home_article': $pre .= $default_pre .= 'home_article.htm'; break; case 'home_comment': $pre .= $default_pre .= 'home_comment.htm'; break; case 'user': $pre .= $default_pre .= 'user.htm'; break; case 'user_login': $pre .= $default_pre .= 'user_login.htm'; break; case 'user_create': $pre .= $default_pre .= 'user_create.htm'; break; case 'user_resetpw': $pre .= $default_pre .= 'user_resetpw.htm'; break; case 'user_resetpw_complete': $pre .= $default_pre .= 'user_resetpw_complete.htm'; break; case 'user_comment': $pre .= $default_pre .= 'user_comment.htm'; break; case 'single_page': $pre .= $default_pre .= 'single_page.htm'; break; case 'search': $pre .= $default_pre .= 'search.htm'; break; case 'operate_sticky': $pre .= $default_pre .= 'operate_sticky.htm'; break; case 'operate_close': $pre .= $default_pre .= 'operate_close.htm'; break; case 'operate_delete': $pre .= $default_pre .= 'operate_delete.htm'; break; case 'operate_move': $pre .= $default_pre .= 'operate_move.htm'; break; case '404': $pre .= $default_pre .= '404.htm'; break; case 'read_404': $pre .= $default_pre .= 'read_404.htm'; break; case 'list_404': $pre .= $default_pre .= 'list_404.htm'; break; default: $pre .= $default_pre .= theme_mode_pre(); break; } if ($config['theme']) { $conffile = APP_PATH . 'view/template/' . $config['theme'] . '/conf.json'; $json = is_file($conffile) ? xn_json_decode(file_get_contents($conffile)) : array(); } !empty($json['installed']) and $path_file = APP_PATH . 'view/template/' . $config['theme'] . '/htm/' . ($id ? $id . '_' : '') . $pre; (empty($path_file) || !is_file($path_file)) and $path_file = APP_PATH . 'view/template/' . $config['theme'] . '/htm/' . $pre; if (!empty($config['theme_child']) && is_array($config['theme_child'])) { foreach ($config['theme_child'] as $theme) { if (empty($theme) || is_array($theme)) continue; $path_file = APP_PATH . 'view/template/' . $theme . '/htm/' . ($id ? $id . '_' : '') . $pre; !is_file($path_file) and $path_file = APP_PATH . 'view/template/' . $theme . '/htm/' . $pre; } } !is_file($path_file) and $path_file = APP_PATH . ($dir ? 'plugin/' . $dir . '/view/htm/' : 'view/htm/') . $default_pre; return $path_file; } function theme_mode_pre($type = 0) { global $config; $mode = $config['setting']['website_mode']; $pre = ''; if (1 == $mode) { $pre .= 2 == $type ? 'portal_category.htm' : 'portal.htm'; } elseif (2 == $mode) { $pre .= 2 == $type ? 'flat_category.htm' : 'flat.htm'; } else { $pre .= 2 == $type ? 'index_category.htm' : 'index.htm'; } return $pre; } ?>ecmascript 6 - How do you polyfill Javascript ES6 `new.target`? - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ecmascript 6 - How do you polyfill Javascript ES6 `new.target`? - Stack Overflow

programmeradmin1浏览0评论

Some ES6 features are really easy to polyfill:

if(!Array.prototype.find){
  Array.prototype.find=...
}

How would you polyfill new.target? It triggers a syntax error when it's used in an unsupported browser. try/catch doesn't work because it's a syntax error. I don't have to use new.target, I'm mostly just curious.

Some ES6 features are really easy to polyfill:

if(!Array.prototype.find){
  Array.prototype.find=...
}

How would you polyfill new.target? It triggers a syntax error when it's used in an unsupported browser. try/catch doesn't work because it's a syntax error. I don't have to use new.target, I'm mostly just curious.

Share Improve this question asked Oct 27, 2015 at 5:00 Leo JiangLeo Jiang 26.3k59 gold badges176 silver badges327 bronze badges 2
  • 5 you can't polyfill new syntax – Jaromanda X Commented Oct 27, 2015 at 5:02
  • 3 And looks like babel doesn't transpile this yet: github./babel/babel/issues/1088 – S McCrohan Commented Oct 27, 2015 at 5:20
Add a ment  | 

3 Answers 3

Reset to default 7

As Jaromanda mented, you cannot polyfill new syntax, but you can easily work around some new.target use cases for now

Taking a look at the new.target docs you'll see some examples that can easily be written with es5

with new.target

function Foo() {
  if (!new.target) throw "Foo() must be called with new";
  console.log("Foo instantiated with new");
}

Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"

without

function Foo() {
  if (!(this instanceof Foo)) throw "Foo() must be called with new";
  console.log("Foo instantiated with new");
}

Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"

with new.target

class A {
  constructor() {
    console.log(new.target.name);
  }
}

class B extends A { constructor() { super(); } }

var a = new A(); // logs "A"
var b = new B(); // logs "B"

without

class A {
  constructor() {
    // class forces constructor to be called with `new`, so
    // `this` will always be set
    console.log(this.constructor.name);
  }
}

class B extends A { constructor() { super(); } }

var a = new A(); // logs "A"
var b = new B(); // logs "B"

Hope this helps a little

Here's a way using Function::bind:

const requireNew = (() => {
  const kFake = {};
  const CtorMap = new WeakMap();
  const FuncToString = function toString () {
    const info = CtorMap.get(this);
    return Function.prototype.toString.apply(
        info ? info.ctor : this, arguments);
  };
  const GetProto = function prototype() {
    const info = CtorMap.get(this);
    return info ? info.ctor.prototype : undefined;
  };
  const SetProto = function prototype(prototype) {
    const info = CtorMap.get(this);
    return !info ? prototype
        : info.wrapper.prototype = info.ctor.prototype = prototype;
  }
  return (Ctor) => {
    const wrapper = function () {
      if (this === kFake) {
        throw new TypeError("Please use 'new' to call this");
      }
      return Ctor.apply(this, arguments);
    }
    wrapper.prototype = Ctor.prototype;
    const bound = wrapper.bind(kFake);
    CtorMap.set(bound, { ctor: Ctor, wrapper });
    Object.defineProperties(bound, {
      prototype: { get: GetProto, set: SetProto,
          enumerable: false, configurable: true },
      name: { value: Ctor.name, writable: false,
          enumerable: false, configurable: true },
      length: { value: Ctor.length, writable: false,
          enumerable: false, configurable: true },
      toString: { value: FuncToString, writable: true,
          enumerable: false, configurable: true }
    });
    return bound;
  }
})();

And here's a simple demo:

function Abc (a) {
  this.a = a;
  console.log("this =", this, "; .a =", this.a, "; new.target:", new.target);
}
const A = requireNew(Abc);
const a = new A(1);

For me it depends what I'm trying to do. The following works in ES3 to get the constructor function that was used with the new operator:

function getTarget(thisObj) {
    for(i in thisObj) {// test thisObj for enumerable properties
        if ({}.hasOwnProperty.call(thisObj, i)) {
            return;// not called with new
        }
    }
    try {
        var i
        ,   C = thisObj.constructor//throws if thisObj is undefined
        if (thisObj instanceof C) return C// throws if not a constructor
    }
    catch(e) {
    }
}

function MyClass() {
    if (getTarget(this)) {
        // `this` is an instance of MyClass or another class if MyClass.call()
        // was used to pass a different subclassed instance as the `this` value
    }
}

But if all I want to know is if MyClass was called with new, then Function.prototype.bind() is a clever way of handling it because bound functions have a bound this arg and therefore when a bound function's this value isn't what it's expected to be, that means it was called with the new operator. this.constructor then contains the new.target value:

function MyClass() {
    if (this===window) {
        console.log('definitely not called with new');
    }
    else {
        console.log('new.target value is ', this.constructor);
    }
}
MyClass = MyClass.bind(window);

var myclass = new MyClass; // new.target value is function MyClass() {...
MyClass.call(myclass);// definitely not called with new

But personally I prefer the first way of doing it so that MyClass.call() can pass a subclassed this value. Technically that would mean that it wasn't necessarily called with new. But before ES6, inheritance was super tricky and you had to use hacks like that to make it work.

发布评论

评论列表(0)

  1. 暂无评论