[从另一个调用JavaScript原型方法

问题描述 投票:2回答:2

这是另一个问题(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;
}

还有其他一些文件,但是我认为错误一定是这两个文件之一。谢谢您的帮助。

javascript methods prototype
2个回答
4
投票

使用当前代码,您可以执行以下操作:

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


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/

或此(当作为参考传递时,bindparticle对象传递到上下文):

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

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

并且它将起作用(或者至少您不会再收到该错误,整个事情是否起作用是另一个问题)。

© www.soinside.com 2019 - 2024. All rights reserved.