ฉันต้องการสร้างฟังก์ชันที่สามารถเรียกใช้งานได้เพียงครั้งเดียวในแต่ละครั้งหลังจากครั้งแรกจะไม่ถูกเรียกใช้งาน ฉันรู้จาก C ++ และ Java เกี่ยวกับตัวแปรคงที่สามารถทำงานได้ แต่ฉันอยากรู้ว่ามีวิธีที่หรูหรากว่านี้หรือไม่?
ฉันต้องการสร้างฟังก์ชันที่สามารถเรียกใช้งานได้เพียงครั้งเดียวในแต่ละครั้งหลังจากครั้งแรกจะไม่ถูกเรียกใช้งาน ฉันรู้จาก C ++ และ Java เกี่ยวกับตัวแปรคงที่สามารถทำงานได้ แต่ฉันอยากรู้ว่ามีวิธีที่หรูหรากว่านี้หรือไม่?
คำตอบ:
หาก "จะไม่ดำเนินการ" คุณหมายความว่า "จะไม่ดำเนินการใด ๆ เมื่อถูกเรียกมากกว่าหนึ่งครั้ง" คุณสามารถสร้างการปิดได้:
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
// do something
}
};
})();
something(); // "do something" happens
something(); // nothing happens
ในคำตอบสำหรับความคิดเห็นโดย @Vladloffe (ขณะนี้ถูกลบไปแล้ว): ด้วยตัวแปรส่วนกลางโค้ดอื่น ๆ สามารถรีเซ็ตค่าของแฟล็ก "ดำเนินการ" ได้ (ชื่อใดก็ตามที่คุณเลือก) ด้วยการปิดรหัสอื่น ๆ จะไม่มีทางทำเช่นนั้นได้ไม่ว่าจะโดยบังเอิญหรือจงใจ
ดังที่คำตอบอื่น ๆ ชี้ให้เห็นในที่นี้หลาย ๆ ไลบรารี (เช่นUnderscoreและRamda ) มีฟังก์ชันยูทิลิตี้เล็กน้อย (โดยทั่วไปชื่อonce()
[*] ) ที่รับฟังก์ชันเป็นอาร์กิวเมนต์และส่งคืนฟังก์ชันอื่นที่เรียกใช้ฟังก์ชันที่ให้มาทุกครั้งโดยไม่คำนึงถึงวิธีการ หลายครั้งที่เรียกใช้ฟังก์ชันที่ส่งคืน ฟังก์ชันที่ส่งคืนยังแคชค่าที่ส่งกลับเป็นครั้งแรกโดยฟังก์ชันที่ให้มาและส่งกลับค่านั้นในการโทรครั้งต่อ ๆ ไป
อย่างไรก็ตามหากคุณไม่ได้ใช้ไลบรารีของบุคคลที่สาม แต่ยังต้องการฟังก์ชั่นยูทิลิตี้ดังกล่าว (แทนที่จะเป็นโซลูชัน nonce ที่ฉันเสนอไว้ข้างต้น) มันก็ง่ายพอที่จะนำไปใช้ รุ่นที่อร่อยที่สุดที่ฉันเคยเห็นคือรุ่นนี้โพสต์โดย David Walsh :
function once(fn, context) {
var result;
return function() {
if (fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
ฉันจะมีแนวโน้มที่จะเปลี่ยนแปลงไปfn = null;
fn = context = null;
ไม่มีเหตุผลสำหรับการปิดเพื่อรักษาการอ้างอิงถึงcontext
เมื่อfn
มีการเรียก
[*] อย่างไรก็ตาม โปรดทราบว่าไลบรารีอื่น ๆ เช่นส่วนขยาย Drupal นี้ไปยัง jQueryอาจมีฟังก์ชันชื่อonce()
ที่ทำสิ่งที่แตกต่างออกไป
if
นิพจน์การทดสอบคำสั่ง) JavaScript คาดว่าค่าใดค่าหนึ่งtrue
หรือfalse
และโฟลว์โปรแกรมจะตอบสนองตามค่าที่พบเมื่อนิพจน์ได้รับการประเมิน ตัวดำเนินการตามเงื่อนไข==
มักจะประเมินเป็นค่าบูลีน ตัวแปรยังสามารถระงับการอย่างใดอย่างหนึ่งหรือtrue
false
(สำหรับข้อมูลเพิ่มเติมโปรดดูเอกสารเกี่ยวกับบูล , truthyและfalsey .)
แทนที่ด้วยฟังก์ชันNOOP ที่ใช้ซ้ำได้(ไม่มีการใช้งาน)
// this function does nothing
function noop() {};
function foo() {
foo = noop; // swap the functions
// do your thing
}
function bar() {
bar = noop; // swap the functions
// do your thing
}
setInterval(foo, 1000)
- และสิ่งนี้ใช้ไม่ได้อีกต่อไป คุณแค่เขียนทับข้อมูลอ้างอิงในขอบเขตปัจจุบัน
function myFunc(){
myFunc = function(){}; // kill it as soon as it was called
console.log('call once and never again!'); // your stuff here
};
<button onClick=myFunc()>Call myFunc()</button>
var myFunc = function func(){
if( myFunc.fired ) return;
myFunc.fired = true;
console.log('called once and never again!'); // your stuff here
};
// even if referenced & "renamed"
((refToMyfunc)=>{
setInterval(refToMyfunc, 1000);
})(myFunc)
setInterval()
) การอ้างอิงจะทำซ้ำฟังก์ชันเดิมเมื่อถูกเรียก
UnderscoreJs มีฟังก์ชันที่ทำเช่นนั้นคือunderscorejs.org/#once
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
once
ยอมรับข้อโต้แย้ง คุณสามารถทำsquareo = _.once(square); console.log(squareo(1)); console.log(squareo(2));
และได้รับสำหรับการโทรทั้ง1
squareo
ฉันเข้าใจถูกไหม
_.once
วิธีนี้ ดูjsfiddle.net/631tgc5f/1
_
; ฉันจะไม่แนะนำให้ขึ้นอยู่กับไลบรารีทั้งหมดสำหรับโค้ดเล็กน้อยเช่นนี้
เมื่อพูดถึงตัวแปรคงนี่ก็เหมือนกับตัวแปรการปิด:
var once = function() {
if(once.done) return;
console.log('Doing this once!');
once.done = true;
};
once(); once();
จากนั้นคุณสามารถรีเซ็ตฟังก์ชั่นได้หากต้องการ:
once.done = false;
var quit = false;
function something() {
if(quit) {
return;
}
quit = true;
... other code....
}
คุณสามารถมีฟังก์ชัน "ลบตัวเอง" ได้
function Once(){
console.log("run");
Once = undefined;
}
Once(); // run
Once(); // Uncaught TypeError: undefined is not a function
แต่นี่อาจไม่ใช่คำตอบที่ดีที่สุดหากคุณไม่ต้องการกลืนกินข้อผิดพลาด
คุณสามารถทำได้:
function Once(){
console.log("run");
Once = function(){};
}
Once(); // run
Once(); // nothing happens
ฉันต้องการให้มันทำงานเหมือนตัวชี้อัจฉริยะหากไม่มีองค์ประกอบจากประเภท A ก็สามารถเรียกใช้งานได้หากมีองค์ประกอบ A อย่างน้อยหนึ่งรายการฟังก์ชันจะไม่สามารถเรียกใช้งานได้
function Conditional(){
if (!<no elements from type A>) return;
// do stuff
}
Once
ส่งผ่านเป็นการโทรกลับ (เช่นsetInterval(Once, 100)
) ฟังก์ชันเดิมจะยังคงถูกเรียกใช้
ลองดู
var fun = (function() {
var called = false;
return function() {
if (!called) {
console.log("I called");
called = true;
}
}
})()
จากเพื่อนบางคนชื่อ Crockford ... :)
function once(func) {
return function () {
var f = func;
func = null;
return f.apply(
this,
arguments
);
};
}
TypeError: Cannot read property 'apply' of null
ดี นั่นคือสิ่งที่คุณได้รับในครั้งที่สองที่คุณเรียกใช้ฟังก์ชันที่ส่งคืน
invalidate
ฟังก์ชันที่ใช้ซ้ำได้ซึ่งทำงานร่วมกับsetInterval
:
var myFunc = function (){
if (invalidate(arguments)) return;
console.log('called once and never again!'); // your stuff here
};
const invalidate = function(a) {
var fired = a.callee.fired;
a.callee.fired = true;
return fired;
}
setInterval(myFunc, 1000);
ลองใช้ JSBin: https://jsbin.com/vicipar/edit?js,console
รูปแบบของคำตอบจาก Bunyk
นี่คือตัวอย่าง JSFiddle - http://jsfiddle.net/6yL6t/
และรหัส:
function hashCode(str) {
var hash = 0, i, chr, len;
if (str.length == 0) return hash;
for (i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
var onceHashes = {};
function once(func) {
var unique = hashCode(func.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]);
if (!onceHashes[unique]) {
onceHashes[unique] = true;
func();
}
}
คุณสามารถทำได้:
for (var i=0; i<10; i++) {
once(function() {
alert(i);
});
}
และจะทำงานเพียงครั้งเดียว :)
ตั้งค่าเริ่มต้น:
var once = function( once_fn ) {
var ret, is_called;
// return new function which is our control function
// to make sure once_fn is only called once:
return function(arg1, arg2, arg3) {
if ( is_called ) return ret;
is_called = true;
// return the result from once_fn and store to so we can return it multiply times:
// you might wanna look at Function.prototype.apply:
ret = once_fn(arg1, arg2, arg3);
return ret;
};
}
หากคุณใช้ Node.js หรือเขียน JavaScript ด้วย browserify ให้พิจารณาโมดูล npm "ครั้งเดียว" :
var once = require('once')
function load (file, cb) {
cb = once(cb)
loader.load('file')
loader.once('load', cb)
loader.once('error', cb)
}
หากคุณต้องการให้สามารถใช้ฟังก์ชันนี้ซ้ำได้ในอนาคตสิ่งนี้จะทำงานได้ดีตามรหัสของ ed Hopp ด้านบน (ฉันรู้ว่าคำถามเดิมไม่ได้เรียกร้องให้มีคุณสมบัติพิเศษนี้!):
var something = (function() {
var executed = false;
return function(value) {
// if an argument is not present then
if(arguments.length == 0) {
if (!executed) {
executed = true;
//Do stuff here only once unless reset
console.log("Hello World!");
}
else return;
} else {
// otherwise allow the function to fire again
executed = value;
return;
}
}
})();
something();//Hello World!
something();
something();
console.log("Reset"); //Reset
something(false);
something();//Hello World!
something();
something();
ผลลัพธ์มีลักษณะดังนี้:
Hello World!
Reset
Hello World!
มัณฑนากรที่เรียบง่ายเขียนง่ายเมื่อคุณต้องการ
function one(func) {
return function () {
func && func.apply(this, arguments);
func = null;
}
}
โดยใช้:
var initializer= one( _ =>{
console.log('initializing')
})
initializer() // 'initializing'
initializer() // nop
initializer() // nop
พยายามใช้ขีดล่างฟังก์ชัน "ครั้งเดียว":
var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.
var init = function() {
console.log("logges only once");
init = false;
};
if(init) { init(); }
/* next time executing init() will cause error because now init is
-equal to false, thus typing init will return false; */
หากคุณใช้ Ramda คุณสามารถใช้ฟังก์ชัน"ครั้งเดียว"ได้
คำพูดจากเอกสารประกอบ:
ครั้งเดียวฟังก์ชั่น (a …→ b) → (a …→ b) PARAMETERS เพิ่มใน v0.1.0
ยอมรับฟังก์ชั่น fn และส่งคืนฟังก์ชันที่ป้องกันการเรียกใช้ fn ซึ่ง fn สามารถถูกเรียกได้เพียงครั้งเดียวไม่ว่าฟังก์ชันที่ส่งคืนจะถูกเรียกใช้กี่ครั้งก็ตาม ค่าแรกที่คำนวณจะส่งคืนในการเรียกใช้ครั้งต่อ ๆ ไป
var addOneOnce = R.once(x => x + 1);
addOneOnce(10); //=> 11
addOneOnce(addOneOnce(50)); //=> 11
ทำให้ง่ายที่สุด
function sree(){
console.log('hey');
window.sree = _=>{};
}
คุณสามารถเห็นผลลัพธ์
this
แทนwindow
JQuery อนุญาตให้เรียกใช้ฟังก์ชันเพียงครั้งเดียวโดยใช้วิธีที่หนึ่ง () :
let func = function() {
console.log('Calling just once!');
}
let elem = $('#example');
elem.one('click', func);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p>Function that can be called only once</p>
<button id="example" >JQuery one()</button>
</div>
การใช้งานโดยใช้วิธี JQuery บน () :
let func = function(e) {
console.log('Calling just once!');
$(e.target).off(e.type, func)
}
let elem = $('#example');
elem.on('click', func);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p>Function that can be called only once</p>
<button id="example" >JQuery on()</button>
</div>
การใช้งานโดยใช้ JS ดั้งเดิม:
let func = function(e) {
console.log('Calling just once!');
e.target.removeEventListener(e.type, func);
}
let elem = document.getElementById('example');
elem.addEventListener('click', func);
<div>
<p>Functions that can be called only once</p>
<button id="example" >ECMAScript addEventListener</button>
</div>
if (!window.doesThisOnce){
function myFunction() {
// do something
window.doesThisOnce = true;
};
};
อันนี้มีประโยชน์ในการป้องกันลูปไม่สิ้นสุด (โดยใช้ jQuery):
<script>
var doIt = true;
if(doIt){
// do stuff
$('body').html(String($('body').html()).replace("var doIt = true;",
"var doIt = false;"));
}
</script>
หากคุณกังวลเกี่ยวกับมลพิษของเนมสเปซให้เปลี่ยนสตริงแบบสุ่มแบบยาวสำหรับ "doIt"
ช่วยป้องกันการดำเนินการที่เหนียว
var done = false;
function doItOnce(func){
if(!done){
done = true;
func()
}
setTimeout(function(){
done = false;
},1000)
}