2011-09-24 37 views
47
// Don't break the function prototype. 
// pd - https://github.com/Raynos/pd 
var proto = Object.create(Function.prototype, pd({ 
    "prop": 42 
})); 

var f = function() { return "is a function"; }; 
f.__proto__ = proto; 

console.log(f.hasOwnProperty("prop")); // false 
console.log(f.prop); // 42 
console.log(f()); // "is a function" 

.__proto__ không chuẩn và không được dùng nữa.Làm cách nào để kế thừa các hàm javascript?

Làm cách nào để kế thừa tạo mẫu đối tượng nhưng có đối tượng đó là hàm.

Object.create trả về đối tượng không phải là chức năng.

new Constructor trả về đối tượng không phải là chức năng.

Động lực: - Một cross-browser finherit

var finherit = function (parent, child) { 
    var f = function() { 
     parent.apply(this, arguments); 
     child.apply(this, arguments); 
    }; 
    f.__proto__ = parent; 
    Object.keys(child).forEach(function _copy(key) { 
     f[key] = child[key]; 
    }); 
    return f; 
}; 

Tôi không tin rằng điều này là có thể, vì vậy chúng tôi có lẽ nên đề xuất một Function.create đến es-thảo luận mailing list

/* 
    Creates a new function whose prototype is proto. 
    The function body is the same as the function fbody. 
    The hash of propertydescriptors props is passed to defineproperties just like 
    Object.create does. 
*/ 
Function.create = (function() { 
    var functionBody = function _getFunctionBody(f) { 
    return f.toString().replace(/.+\{/, "").replace(/\}$/, ""); 
    }; 
    var letters = "abcdefghijklmnopqrstuvwxyz".split(""); 

    return function _create(proto, fbody, props) { 
    var parameters = letters.slice(0, fbody.length); 
    parameters.push(functionBody(fbody)); 
    var f = Function.apply(this, parameters); 
    f.__proto__ = proto; 
    Object.defineProperties(f, props); 
    return f; 
    }; 
})(); 

Related es-discuss mail

Như đã đề cập trong chuỗi thảo luận có tồn tại một ES: strawman <| nhà điều hành nguyên mẫu sẽ cho phép điều này.

Chúng ta hãy xem những gì nó sẽ trông giống như sử dụng <|

var f1 = function() { 
    console.log("do things"); 
}; 

f1.method = function() { return 42; }; 

var f2 = f1 <| function() { 
    super(); 
    console.log("do more things"); 
} 
console.log(f1.isPrototypeOf(f2)); // true 
console.log(f2()); // do things do more things 
console.log(f2.hasOwnProperty("method")); // false 
console.log(f2.method()); // 42 
+0

'console.log (f.apply);' sẽ trả về 'undefined' ... vì vậy cách này không tối ưu theo bất kỳ cách nào ... vẫn đang nghĩ về giải pháp thay thế. –

+0

@ FelixKling Tôi quên rằng tôi nên kế thừa từ 'Function.prototype' cảm ơn – Raynos

+0

Có [câu hỏi này] (http://stackoverflow.com/questions/6564481/javascript-polymorphic-callable-objects) Tôi gặp phải một lúc trở lại người hỏi đã tham chiếu [bài viết này] (http://ajaxian.com/archives/javascript-tips-for-rookies-and-gurus) cho thấy một mẫu để tạo * "các đối tượng có thể gọi được đa hình" *. Không chắc chắn nếu đó là những gì bạn đang sau, nhưng có thể có giá trị một cái nhìn. – user113716

Trả lời

9

Tôi hy vọng rằng tôi hiểu được quyền này.

Tôi tin rằng bạn muốn một functor đó là cả một ví dụ của một nguyên mẫu được xác định trước (có, một lớp học, không phải là một lớp học cổ điển) cũng như trực tiếp gọi? Đúng? Nếu vậy, thì điều này có ý nghĩa hoàn hảo và rất mạnh mẽ và linh hoạt (đặc biệt là trong một môi trường không đồng bộ cao như JavaScript). Đáng buồn là không có cách nào để làm điều đó thanh lịch trong JavaScript mà không cần thao tác __proto__. Bạn có thể làm điều đó bằng cách bao gồm một hàm ẩn danh và sao chép tất cả các tham chiếu đến tất cả các phương thức (mà dường như là hướng bạn đang hướng đến) để hoạt động như một lớp proxy. Nhược điểm của việc này là ...

  1. Nó rất tốn kém về thời gian chạy.
  2. (functorObj instanceof MyClass) sẽ không bao giờ là true.
  3. Thuộc tính sẽ không thể truy cập trực tiếp (nếu tất cả được gán bằng tham chiếu thì đây sẽ là một câu chuyện khác, nhưng nguyên thủy được gán theo giá trị). Điều này có thể được giải quyết với accessors thông qua defineProperty hoặc chỉ đơn giản là tên accessor phương pháp nếu cần thiết (nó xuất hiện rằng đó là những gì bạn đang tìm kiếm, chỉ cần thêm tất cả các tài sản cho functor với defineProperty thông qua getters/setters thay vì chỉ chức năng nếu bạn không cần hỗ trợ đa công cụ/khả năng tương thích ngược).
  4. Bạn có khả năng chạy vào các trường hợp cạnh mà nguyên mẫu gốc cuối cùng (như Object.prototype hoặc Array.prototype [nếu bạn kế thừa]] có thể không hoạt động như mong đợi.
  5. Calling functorObj(someArg) sẽ luôn làm bối cảnh this là đối tượng, không phân biệt nếu nó được gọi là functorObj.call(someOtherObj, someArg) (đây không phải là trường hợp cho phương pháp gọi là mặc dù)
  6. Bởi vì đối tượng functor được tạo ra vào thời điểm yêu cầu, nó sẽ được bị khóa trong thời gian và thao tác nguyên mẫu ban đầu sẽ không ảnh hưởng đến các đối tượng functor được phân bổ như đối tượng bình thường sẽ bị ảnh hưởng (sửa đổi MyClass.prototype sẽ không ảnh hưởng đến bất kỳ đối tượng functor nào và ngược lại cũng đúng).

Nếu bạn sử dụng nó một cách nhẹ nhàng, không có điều nào trong số này phải là vấn đề lớn.

Trong nguyên mẫu của buổi học của bạn xác định một cái gì đó giống như ...

// This is you're emulated "overloaded" call() operator. 
MyClass.prototype.execute = function() { 
    alert('I have been called like a function but have (semi-)proper access to this!'); 
}; 

MyClass.prototype.asFunctor = function(/* templateFunction */) { 
    if ((typeof arguments[0] !== 'function') && (typeof this.execute !== 'function')) 
     throw new TypeError('You really should define the calling operator for a functor shouldn\'t you?'); 
    // This is both the resulting functor proxy object as well as the proxy call function 
    var res = function() { 
     var ret; 
     if (res.templateFunction !== null) 
     // the this context here could be res.asObject, or res, or whatever your goal is here 
     ret = res.templateFunction.call(this, arguments); 
     if (typeof res.asObject.execute === 'function') 
     ret = res.asObject.execute.apply(res.asObject, arguments); 
     return ret; 
    }; 
    res.asObject = this; 
    res.templateFunction = (typeof arguments[0] === 'function') ? arguments[0] : null; 
    for (var k in this) { 
     if (typeof this[k] === 'function') { 
     res[k] = (function(reference) { 
      var m = function() { 
       return m.proxyReference.apply((this === res) ? res.asObject : this, arguments); 
      }; 
      m.proxyReference = reference; 
      return m; 
     })(this.asObject[k]); 
     } 
    } 
    return res; 
}; 

Kết quả sử dụng sẽ giống như ...

var aobj = new MyClass(); 
var afunctor = aobj.asFunctor(); 
aobj.someMethodOfMine(); // << works 
afunctor.someMethodOfMine(); // << works exactly like the previous call (including the this context). 
afunctor('hello'); // << works by calling aobj.execute('hello'); 

(aobj instanceof MyClass) // << true 
(afunctor instanceof MyClass) // << false 
(afunctor.asObject === aobj) // << true 

// to bind with a previous function... 
var afunctor = (new MyClass()).asFunctor(function() { alert('I am the original call'); }); 
afunctor() // << first calls the original, then execute(); 
// To simply wrap a previous function, don't define execute() in the prototype. 

Bạn có thể thậm chí chuỗi liên kết vô số các đối tượng khác/chức năng/vv cho đến khi bò về nhà. Chỉ cần cấu trúc lại cuộc gọi proxy một chút.

Hy vọng điều đó sẽ hữu ích. Oh, và tất nhiên bạn có thể thay đổi dòng chảy của nhà máy sao cho một nhà xây dựng được gọi là không có toán tử new sau đó khởi tạo một đối tượng mới và trả về đối tượng functor. Tuy nhiên bạn thích (bạn chắc chắn có thể làm điều đó theo cách khác quá).

Cuối cùng, để có bất kỳ hàm nào trở thành toán tử thực thi cho hàm functor một cách thanh lịch hơn, chỉ cần thực hiện hàm proxy một phương thức Function.prototype và chuyển đối tượng đó để bọc nếu bạn muốn làm một cái gì đó như (bạn sẽ phải trao đổi với templateFunctionthisthis với lập luận tất nhiên) ...

var functor = (function() { /* something */ }).asFunctor(aobj); 
+3

Tuyệt. Tôi chưa bao giờ nghe nói về các functors. Tôi chắc chắn sẽ đặt tên cho chú chó kế tiếp của tôi. – Ben

+0

Điều này thực sự là một functor giả (có, funny tên) proxying tất cả mọi thứ. Đó là một cách cẩu thả để làm cho nó xảy ra. Một số ngôn ngữ hỗ trợ điều này một cách tự nhiên. Nếu EMCAScript5 cho phép thao tác '__proto__' (hoặc một tiêu chuẩn thay thế tương đương), ** hoặc ** quá tải của toán tử, thì JavaScript cũng vậy. Đáng buồn là nó không. Đối với tất cả sức mạnh và tính linh hoạt của nó ... thở dài. –

+0

@ mr.stobbe bạn có thể sử dụng proxy ES6 để thực hiện việc này không? (Làm việc trong chrome & FF). Hoặc bạn có thể gian lận và giả vờ '<|' tồn tại ('protoOf = function (proto, điều) {điều .__ proto__ = proto;}') – Raynos

Các vấn đề liên quan