JS 浅拷贝——Shallow copy 在 Canvas 绘图中的应用
(一)简述 JavaScript 浅拷贝 和 深拷贝
在介绍浅拷贝和深拷贝的概念之前,我们不妨先了解一下 JavaScipt 变量的两种保存方式。
ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。5 种基本数据类型:Undefined、Null、Boolean、Number 和 String。这 5 种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。引用类型的值是保存在内存中的对象。与其他语言不同,JavaScript 不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。
1.1 浅拷贝 Shallow copy
- 对于基本类型的值,进行浅拷贝时,将一个变量复制给另一个变量,相当于创建了一个新值,两个变量是独立的,不会互相影响。举个栗子:
var a = 2;
var b = a;
a = 5;
console.log(a); //5
console.log(b); //2
- 对于引用类型值,进行浅拷贝时,实际上只是将操作的对象的引用复制给另一个变量,两个变量指向同一个内存中的变量,修改其中任意一个变量将影响另一个变量,思考下面的例子:
var obj1 = new Object({'name' : 'John', 'age' : 19});
var obj2 = obj1;
obj1.name = 'Sara';
console.log(obj1); //{'name' : 'Sara', 'age' : 19}
console.log(obj2); //{'name' : 'Sara', 'age' : 19}
obj2.age = 30;
console.log(obj1); //{'name' : 'Sara', 'age' : 30}
console.log(obj2); //{'name' : 'Sara', 'age' : 30}
上面例子中,无论修改任何一个变量,都会直接修改这两个变量共同指向的内存中的对象,因此,两个变量的值同时改变。
(二)浅拷贝在Canvas绘图中的应用
我们使用Canvas绘图时,经常会遇到改变所绘图形的属性问题,比如控制位置的坐标点。使用浅拷贝,可以有效地节省代码,减少遍历次数,提高代码执行效率。
下面的例子将重点展示:浅拷贝在拖拽图形时如何改变图形的坐标属性。
如上图所示,图中有三个多边形,我们移动其中一个红色的多边形,改变了红色多边形的坐标属性。
其中的逻辑是:当鼠标点击canvas画布时,判断鼠标是否落在多边形范围内,如果是,则找出这个多边形;并且将这个多边形对象浅拷贝给一个变量dragging;当鼠标移动时,实时改变dragging变量中的x,y属性,此时多边形对象的属性x, y也同时改变。
我们观察其中一个代码片段,思考其中应用的浅拷贝的原理:
下面代码节选自《Core HTML5 Canvas Graphics, Animation, and Game Development》。
// Event handlers.....................................................
//鼠标点击画布时...
canvas.onmousedown = function (e) {
var loc = windowToCanvas(e.clientX, e.clientY); //获取当前鼠标位置
e.preventDefault(); // prevent cursor change
if (editing) { //进入编辑模式...
polygons.forEach( function (polygon) { //遍历所有的多边形对象组成的数组polygons
polygon.createPath(context); //绘制路径,但不描边
if (context.isPointInPath(loc.x, loc.y)) { //判断鼠标位置是否落在多边形范围内
saveDrawingSurface(); //储存画布信息
mousedown.x = loc.x; //储存鼠标信息
mousedown.y = loc.y;
dragging = polygon; //将当前选中的多边形对象浅拷贝给dragging
draggingOffsetX = loc.x - polygon.x; //储存鼠标距离多边形中心点的偏移值
draggingOffsetY = loc.y - polygon.y;
return;
}
});
}
else { //进入绘图模式...
startDragging(loc);
dragging = true;
}
};
//鼠标移动时......
canvas.onmousemove = function (e) {
var loc = windowToCanvas(e.clientX, e.clientY);
e.preventDefault(); // prevent selections
if (editing && dragging) { //进行编辑模式并选中多边形
dragging.x = loc.x - draggingOffsetX; //修改dragging.x值,直接修改了选中多边形的x属性
dragging.y = loc.y - draggingOffsetY; //修改dragging.y值,直接修改了选中多边形的y属性
context.clearRect(0, 0, canvas.width, canvas.height); //清除画布
drawGrid('lightgray', 10, 10); //绘制网格线
drawPolygons(); // 重新绘制所有的多边形
}
else { //略过......
if (dragging) {
restoreDrawingSurface();
updateRubberband(loc, sides, startAngle);
if (guidewires) {
drawGuidewires(mousedown.x, mousedown.y);
}
}
}
};
将选中的多边形对象复制给dragging变量,当修改dragging变量时,同时也改变了选中多边形的属性,节省了代码,避免了多次遍历数组,提高代码执行的效率。
完整示例请查阅Github源码:https://github.com/corehtml5canvas/code/tree/master/ch02/example-2.28
本文地址:https://blog.csdn.net/m0_37128507/article/details/85785373