这是另一个问题的重复(8778874)。但是,接受的答案对我不起作用,请帮我找错。
我有一个带有多个原型方法的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;
}
还有一些其他文件,但我认为错误必须是这两个中的一个。谢谢你的帮助。
答案 0 :(得分:3)
使用您当前的代码,您可以这样做:
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()
。
仅供参考,类似的问题和答案在这里:How to get callback to work with "this" in class scope
答案 1 :(得分: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/
它会起作用(或者至少你不会再犯这个错误,整个过程是否有效)。