2015-09-14 6 views
1

Это дубликат другого вопроса (8778874). Однако принятый ответ не работает для меня, пожалуйста, помогите мне найти мою ошибку.вызов метода прототипа javascript от другого

У меня есть класс javascript с несколькими методами прототипа, но по какой-то причине я не могу назвать один прототип из другого.

var Particles = function() {}; 

Particles.prototype.physicsStep = function() { 
    this.computeForces(); // error in Chrome 
    this.integrationStep(); // error in Chrome 
} 

Particles.prototype.computeForces = function() { 
    // do some stuff 
} 

Particles.prototype.integrationStep = function() { 
    // do some stuff 
} 

Chrome всегда выдает ошибку "Неперехваченные TypeError: this.computeForces не является функцией". Мне, должно быть, не хватает цели. Любая помощь приветствуется.

particles.js

// define particles class 
var Particles = function() { 
    // program constants 
    this.dt = 0.01; 
    this.NUM_CIRCLES = 50; 
    this.DRAG_COEFF = 1; 
    this.SIMILARITY_COEFF = 0.05; 
    this.INTER_CIRCLE_ALPHA = 1000; 
    this.INTER_CIRCLE_GAMMA = 1; 
    this.BOUNDARY_ALPHA = 100; 
    this.BOUNDARY_GAMMA = 2; 
    this.MAX_FORCE = 10; 
    this.MASS_SCALE = 5; 
    this.STATE_LPF_POL = 0.01; 
    this.MODEL_UPDATE_INTERVAL_SECONDS = 1; 
    // simulation state 
    this.t = null; 
    this.positions = null; 
    this.old_positions = null; 
    this.radii = null; 
    this.velocities = null; 
    this.forces = null; 
    this.boundaries = {x: null, y: null}; 
}; 



////////////////////////////////////////////////////////////////////////////////////////////// 
// Public Interface 
////////////////////////////////////////////////////////////////////////////////////////////// 

/* 
physicsStep() 
------------- 
step forward the particle simulation. 
*/ 
Particles.prototype.physicsStep = function() { 
    this.computeForces(); 
    this.integrationStep(); 
} 

/* 
initState() 
----------- 
initialize physics state to all zeros. 
*/ 
Particles.prototype.initState = function() { 
    this.t = 0; 
    this.boundaries = {x: [0, 1], y: [0, 1]}; 
    this.positions = []; 
    this.old_positions = []; 
    this.radii = []; 
    this.velocities = []; 
    this.forces = []; 
    for(i = 0; i < this.NUM_CIRCLES; i++) { 
     this.positions.push(new THREE.Vector2(0,0)); 
     this.old_positions.push(new THREE.Vector2(0,0)); 
     this.radii.push(0); 
     this.velocities.push(new THREE.Vector2(0,0)); 
     this.forces.push(new THREE.Vector2(0,0)); 
    } 
} 

/* 
initModel() 
----------- 
initialize model parameters to zeros. 
*/ 
Particles.prototype.initModel = function() { 
    // initialize the model 
    this.similarities = []; 
    for(i = 0; i < this.NUM_CIRCLES; i++) { 
     this.similarities.push([]); 
     for(j = 0; j < this.NUM_CIRCLES; j++) { 
      this.similarities[i].push(0); 
     } 
    } 
} 

/* 
updateModel() 
------------- 
get new parameters for the model. 
currently implemented with placeholder random update. 
*/ 
Particles.prototype.updateModel = function() { 
    for(i = 0; i < this.NUM_CIRCLES; i++) { 
     for(j = i+1; j < this.NUM_CIRCLES; j++) { 
      // place holder for now 
      this.similarities[i][j] = (1 - 2*Math.random()); 
     } 
    } 
} 

/* 
setBoundaries(xlims, ylims) 
--------------------------- 
sets the x and y boundaries for the particle simulation. 
xlims is [left, right]. 
yllims is [bottom, top]. 
*/ 
Particles.prototype.setBoundaries = function(xlims, ylims) { 
    if(xlims != null) this.boundaries.x = xlims; 
    if(ylims != null) this.boundaries.y = ylims; 
} 

/* 
randomizeState() 
---------------- 
randomizes the state of the simulation. 
*/ 
Particles.prototype.randomizeState = function() { 
    this.t = 0; 
    for(i = 0; i < this.NUM_CIRCLES; i++) { 
     var xrange = this.boundaries.x[1] - this.boundaries.x[0]; 
     var yrange = this.boundaries.y[1] - this.boundaries.y[0]; 
     this.positions[i].x = this.boundaries.x[0] + xrange * Math.random(); 
     this.positions[i].y = this.boundaries.y[0] + yrange * Math.random(); 
     this.old_positions[i].x = this.positions[i].x; 
     this.old_positions[i].y = this.positions[i].y; 
     this.velocities[i].x = 0; 
     this.velocities[i].y = 0; 
     this.radii[i] = 0.1 * Math.min(xrange, yrange) * Math.random(); 
    } 
} 

////////////////////////////////////////////////////////////////////////////////////////////// 
// Helpers 
////////////////////////////////////////////////////////////////////////////////////////////// 

/* 
computeForces() 
--------------- 
gets the forces for the next time step. 
*/ 
Particles.prototype.computeForces = function() { 
    // per-particle forces 
    var alpha = this.BOUNDARY_ALPHA; 
    var gamma = this.BOUNDARY_GAMMA; 
    for(i = 0; i < this.NUM_CIRCLES; i++) { 
     // start at 0 
     this.forces[i].x = 0; 
     this.forces[i].y = 0; 
     // force exerted by boundaries 
     this.forces[i].add(FORCES.boundaryForce(this.positions[i], this.radii[i], this.boundaries.x, this.boundaries.y, alpha, gamma)); 
     // drag force 
     this.forces[i].add(FORCES.dragForce(this.velocities[i], this.DRAG_COEFF)); 
    } 
    // inter-particle forces 
    alpha = this.INTER_CIRCLE_ALPHA; 
    gamma = this.INTER_CIRCLE_GAMMA; 
    for(i = 0; i < this.NUM_CIRCLES; i++) { 
     for(j = i+1; j < this.NUM_CIRCLES; j++) { 
      // proximity repulsion force 
      var repulsion = FORCES.forceBetweenCircles(this.positions[i], this.radii[i], this.positions[j], this.radii[j], alpha, gamma); 
      // similarity attraction/repulsion force 
      var similarity = this.similarities[i][j] * this.SIMILARITY_COEFF; 
      repulsion.add(FORCES.similarityForce(this.positions[i], this.radii[i], this.positions[j], this.radii[j], similarity)); 
      // add the forces to both particles 
      this.forces[i].add(repulsion); 
      this.forces[j].add(repulsion.negate()); 
     } 
    } 
    // make sure no forces exceed maximum 
    for(i=0; i < this.NUM_CIRCLES; i++) { 
     if(this.forces[i].length() > this.MAX_FORCE) { 
      this.forces[i].normalize().multiplyScalar(this.MAX_FORCE); 
     } 
    } 
} 

/* 
integrationStep() 
----------------- 
update based position and velocity based on forces 
*/ 
Particles.prototype.integrationStep = function() { 
    for(i = 0; i < this.NUM_CIRCLES; i++) { 
     var mass = this.radii[i] * this.MASS_SCALE; 
     var a = new THREE.Vector2(this.forces[i].x/mass, this.forces[i].y/mass); 
     var pos = new THREE.Vector2(this.positions[i].x, this.positions[i].y); 
     // verlet integration 
     pos.multiplyScalar(2).sub(this.old_positions[i]).add(a.multiplyScalar(Math.pow(this.dt, 2))); 
     // lowpass filter 
     pos.addVectors(this.positions[i], pos.sub(this.positions[i]).multiplyScalar(1 - this.STATE_LPF_POLE)); 
     // update state 
     this.velocities[i].subVectors(pos, this.old_positions[i]).divideScalar(2 * this.dt); 
     this.old_positions[i] = this.positions[i]; 
     this.positions[i] = pos; 
    } 
    this.t += this.dt; 
} 


////////////////////////////////////////////////////////////////////////////////////////////// 

render.js

// opengl variables 
var camera, scene, renderer, width, height, res; 
// shader variables 
var uniforms; 
// particles 
var particles = new Particles(); 

var CONSTANTS = { 
    PI: Math.PI, 
    dt: 0.01, 
    NUM_CIRCLES: 50, 
    BORDER_PERCENT: 0.1 
} 

// initialize 
init(); 
// kick off physics 
window.setInterval(particles.physicsStep, 1000 * particles.dt); 
// kick off model parameter update 
//window.setInterval(updateModel, 1000 * CONSTANTS.MODEL_UPDATE_INTERVAL_SECONDS); 
animate(); 

/* 
init() 
------ 
*/ 
function init() { 
    particles.initState(); 
    particles.initModel(); 
    initCameraAndScene(); 
    initRenderer(); 
    particles.randomizeState(); 
} 

/* 
animate() 
--------- 
*/ 
function animate() { 
    requestAnimationFrame(animate); 
    render(); 
} 

/* 
render() 
-------- 
*/ 
function render() { 
    updateUniforms(); 
    renderer.render(scene, camera); 
} 


/* 
initCameraAndScene() 
----------- 
setup scene and fullscreen quad. 
*/ 
function initCameraAndScene() { 
    // initialize camer 
    camera = new THREE.Camera(); 
    camera.position.z = 1; 
    // make a scene... 
    scene = new THREE.Scene(); 
    // fullscreen quad 
    var geometry = new THREE.PlaneBufferGeometry(2,2); 
    var material = getShaderMaterial(); 
    var mesh = new THREE.Mesh(geometry, material); 
    scene.add(mesh); 
} 

/* 
initRenderer() 
-------------- 
initialize the opengl renderer element. 
*/ 
function initRenderer() { 
    renderer = new THREE.WebGLRenderer(); 
    renderer.setPixelRatio(window.devicePixelRatio); 
    document.body.appendChild(renderer.domElement); 
    onWindowResize(); 
    window.addEventListener('resize', onWindowResize, false); 
    window.addEventListener('click', onMouseClick, false); 
} 

/* 
onWindowResize(event) 
--------------------- 
windows resize event handler. updates shader uniforms 
as necessary. 
*/ 
function onWindowResize(event) { 
    // hack intended to get rid of scrollable area 
    width = Math.max(0, window.innerWidth - 20); 
    height = Math.max(0, window.innerHeight - 20); 
    // end of hack 
    renderer.setSize(width, height); 
    res = width/height; 
    particles.setBoundaries([0, res], null); 
} 

/* 
onMouseClick(event) 
------------------- 
mouseclick event handler. randomize state. 
*/ 
function onMouseClick(event) { 
    particles.updateModel(); 
} 

/* 
getShaderMaterial() 
--------------- 
returns a THREE.ShaderMaterial compiled from the 
shader strings found in SHADERS.vertexShader and 
SHADERS.fragmentShader. 
*/ 
function getShaderMaterial() { 
    // this string holds #defined constants 
    var constants = ""; 
    for(var key in CONSTANTS) { 
     constants = constants.concat("#define "); 
     constants = constants.concat(key + " " + CONSTANTS[key]); 
     constants = constants.concat("\n"); 
    } 
    // shader variables 
    uniforms = { 
     screenwidth: {type: "f", value: window.innerWidth}, 
     screenheight: {type: "f", value: window.innerHeight}, 
     t: {type: "f", value: 0}, 
     centers: {type: "v2v", value: []}, 
     radii: {type: "fv1", value: []} 
    }; 
    // make the material 
    var material = new THREE.ShaderMaterial({ 
     uniforms: uniforms, 
     vertexShader: constants + SHADERS.vertexShader, 
     fragmentShader: constants + SHADERS.fragmentShader 
    }); 
    return material; 
} 

/* 
updateUniforms() 
---------------- 
sets the shader uniforms based on the current simulation state. 
*/ 
function updateUniforms() { 
    uniforms.t.value = particles.t; 
    uniforms.screenwidth.value = width; 
    uniforms.screenheight.value = height; 
    uniforms.centers.value = particles.positions; 
    uniforms.radii.value = particles.radii; 
} 

есть некоторые другие файлы, а также, но я думаю, что ошибка должна быть в одном из этих двух. спасибо за помощь.

+2

Как создать экземпляр и как вы называете методы? * этот * задается вызовом. – RobG

+0

в другом файле i use 'particle = new Particles();' а затем «particle.physicsStep()» – applecollider

+2

Я не получаю сообщение об ошибке [this] (http://jsfiddle.net/6t5r6o5u/). EDIT: опубликуйте свой фактический код (соответствующие фрагменты), в том числе в каких файлах «Particles» объявлен и вызван. –

ответ

2

С текущего кода, вы можете просто сделать это:

var p = new Particles(); 
p.physicsStep(); 

И тогда внутри physicsStep(), он будет соответствующим образом выполнять this.computeForces() и this.integrationStep() и this будет указатель на p объект, который был создан в первая строка кода выше.

Значение this устанавливается как метод/функция называется as described here, так что если у вас возникли проблемы со значением this, то проблема, скорее всего, не в самом методе, но и в том, как она вызывается.

Если вы хотите получить помощь с этой частью своего кода, то можете добавить этот код к своему вопросу, и давайте посмотрим.

Рабочий пример: http://jsfiddle.net/jfriend00/yuxppyyf/


Да, вы правы, одна проблема не должны делать с тем, как вы используете setInterval.

Вы можете изменить это:

window.setInterval(particles.physicsStep, 1000 * particles.dt); 

к этому:

window.setInterval(particles.physicsStep.bind(particles), 1000 * particles.dt); 

Когда вы передаете particles.physicsStep как функцию ссылки на другую функцию, то particles часть этого теряется. Все, что передается, является ссылкой на метод physicsStep, поэтому, когда setInterval затем вызывает его, он вызывается как обычная функция, а не как метод вашего объекта.

Это распространенная ошибка в Javascript, и есть несколько способов решить эту проблему. Я показал с использованием .bind() выше.Вы также можете сделать свою собственную небольшую функцию заглушки (которая, по сути, что .bind() делает для вас), как это:

window.setInterval(function() { 
    particles.physicsStep(); 
}, 1000 * particles.dt); 

Это гарантирует, что physicStep() вызывается правый объект.

FYI, аналогичная проблема и ответ здесь: How to get callback to work with "this" in class scope

+0

спасибо. после прочтения этой ссылки и нескольких других я предполагаю, что проблема связана с тем, как обработчик событий для 'window.setInterval' настраивает' this', но я недостаточно разбираюсь в механизмах, чтобы увидеть решение. Я очень ценю всяческую помощь. – applecollider

+0

@applecollider - да, вы правы в 'setInterval()'. Посмотрите, что я добавил в свой ответ, когда вы раскрыли эту часть своего кода. – jfriend00

1

Изменить эту строку:

window.setInterval(particles.physicsStep, 1000 * particles.dt); 

http://jsfiddle.net/6t5r6o5u/3/

Где вы передаете ссылку метод функции, а не сам объект, а также:

window.setInterval(function(){particles.physicsStep()}, 1000 * particles.dt); 

http://jsfiddle.net/6t5r6o5u/2/

Или это (что bind ВЛЯЕТСЯ particle объект в контексте, когда передается в качестве ссылки):

window.setInterval(particles.physicsStep.bind(p), 1000); 

http://jsfiddle.net/6t5r6o5u/4/

И он будет работать (или, по крайней мере, вы не получите, что ошибка, все ли работает, это другой вопрос).

Смежные вопросы