Javascript 基础知识,ajax,面向对象和第三方模块
Javascript
数据类型
-
数据类型
-
基本数据类型 数字,字符串,布尔值 undefine null
undefined的常见情形:ES6新语法提供的对象解构赋值应用讲解
1.1 对象属性不存在 var obj ; console.log(obj.test)
1.2 函数没有返回值
1.3 函数的实参少于形参数
null是只有一个值的数据类型表示一个空对象(引用指针)
用typeof会返回object数据类型,下面是null的三种常见情形
1.4 获取dom元素没有获取到指定的元素对象
1.5 给变量赋值为Null
1.6 使用正则进行匹配,如果匹配无结果返回null
null和undefined
相同点
2者转换成布尔值都是false,无法进行区分,并且无法转换成obj类型,更无法调用属性,会报错
不同点:
数字类型
字符串类型 -
-
引用数据类型 object
ES6
var的缺点 以及let的变量定义可以规避以下缺点
- var会有预解析,在声明变量之前,就可以使用,只不过变量值为undefinded
console.log(a); var a=10;
- var 可以重复定义
- 作用于问题,es5只有全局和函数作用域,没有块作用域,比如下面
{
var a = 1
}
console.log(a) //这里a不应该被输出,因为a是块作用域里的变量
- for玄幻中的全局变量污染
var i
for (i=1;i<20;i++){
console.log(i)
}
console.log(i) // 这里不应该输出for循环里的i
const定义常量
有以下四个特点
1.一旦声明必须赋值
2.一旦赋值不能更改
3.有作用域,包括块级作用域
4.声明引用类型的值可以修改
1-3略,4的代码验证如下
const obj = {
"name":"alex",
"age":18
};
//obj = {"age":18}; Assignment to constant variable. 这里修改obj会报错,但是修改obj的值却可以,本质原因修改obj是修改引用常量的内存地址,而obj.name只是改引用地址里的一个值.
obj.name = "shi";
console.log(obj)
es6下的字符串拼接 可以用`${}`进行拼接字符串,而且新语法支持字符串里换行
let name = "alex"
console.log(`他的名字叫${name},他今年10岁`)
es6新语法 结构语法
对象结构赋值
ES6新语法提供的对象解构赋值应用讲解.
如果有需求 把name,age,rdm单独提取出来
ES5语法是
var name,age,rdm;
name=obj.name;
age=obj.age;
rdm=obj.rdm;
console.log(rdm);
ES6语法可以简写成,因为是对象结构,因此不存在顺序问题,只要对象的key值一一对应就行
let {name,age,rdm} = obj;
console.log(age,rdm);
结构中还可以重新赋值变量名,在提取对象属性同时,把属性赋值给新的变量myname,myage,myrdm
let {name:myname,age:myage,rdm:myrdm} = obj;
console.log(myname,myrdm);
数组结构赋值
数组解构原理是利用元素的索引位置进行一一对应
arr = [1,2,3];
let [a,b,c] = arr;
这里的abc刚好对应数组的3个元素,因此称为完全结构
let [a,b,c,d] = arr;
d因为没有对应值,因此为undefined,这种不一一对应称为部分结构
部分结构还可以值对应其中几个元素
let[,,c]=arr;
这里只输出第三个元素
数组的复核结构
arr = [1,2,3,[100,200,300]]
let [a,b,c,[x,y,z]] = arr;
console.log(a,b,c,x,y,z)
数组结构还可以用来数据交换
let str1="111";
let str2="222";
[str1,str2]=[str2,str1];
console.log(str1,str2);
es6新语法 对象的简化写法
let name = "alex",age = 19;
function fn() {
console.log(this.name)
}
//声明一个对象,对象内部的key就是变量名,
let obj = {name,age,fn};
obj.fn(); //这里因为fn是obj的属性,是obj调用的,因此this指向的是obj
js的导入导出
在 JavaScript 模块(ES6)中,export 和 export default 是用来导出模块的两种不同语法,它们在如何被导入时有所区别。
export导出
当你使用 export 导出函数时,你可以在其他文件中使用具名(named)导入来导入这个函数。你可以从同一个模块中导出多个具名的值,然后在导入时需要使用相同的名称或者使用解构来指定你想要的具体项。举个例子
// module.js
export function myFunction() {
// Function implementation
}
export const myVariable = 123;
export class MyClass { /* ... */ }
导入时,你将使用大括号 {} 来指定你想要导入的具名导出:
// main.js
import { myFunction, myVariable, MyClass } from './module.js';
export default导出
export default 允许你设置一个默认导出的值。每个模块只能有一个默认导出,而其他文件可以使用默认导入语法来导入这个值,不需要使用大括号 {},并且可以给导入的值指定任何名称。举个例子:
// module.js
export default function myFunction() {
// Function implementation
}
导入时,你不需要大括号 {},并且可以自由命名:
// main.js
import anyNameYouLike from './module.js';
// 调用导入的函数
anyNameYouLike();
在这个例子中,anyNameYouLike 是你为默认导入的 myFunction 函数指定的名字,你可以随意命名。
总结
使用 export 可以导出多个值,并且在导入时需要使用相同的名称(或者使用 as 关键字重命名)。
使用 export default 可以导出一个默认值,每个模块只能有一个默认导出,导入时不需要使用相同的名称,可以任意命名。
export 和 export default 可以在同一个模块中同时使用,但是通常,一个模块只会有一个默认导出,而对于具名导出则可能有多个。选择使用哪种方式取决于你如何组织你的代码以及你的个人偏好。
对象的几种遍历方法
使用 for...in 循环
for...in 循环可以遍历一个对象的所有可枚举属性(包括继承的属性)。
const object = { a: 1, b: 2, c: 3 };
for (const key in object) {
if (object.hasOwnProperty(key)) { // 只遍历对象自身的属性,不遍历继承的属性
console.log(key, object[key]);
}
}
使用 Object.keys() 方法
Object.keys() 返回一个数组,包含对象自身的所有可枚举属性名称。
const object = { a: 1, b: 2, c: 3 };
Object.keys(object).forEach((key) => {
console.log(key, object[key]);
});
使用 Object.entries() 方法
Object.entries() 返回一个给定对象自身可枚举属性的键值对数组。
const object = { a: 1, b: 2, c: 3 };
Object.entries(object).forEach(([key, value]) => {
console.log(key, value);
});
使用 Object.getOwnPropertyNames() 方法
Object.getOwnPropertyNames() 返回一个数组,包含对象自身的所有属性(无论是否可枚举)的名称。
const object = { a: 1, b: 2, c: 3 };
Object.getOwnPropertyNames(object).forEach((key) => {
console.log(key, object[key]);
});
注意:使用 Object.getOwnPropertyNames() 时,你会得到对象上所有自身的属性,包括不可枚举的属性。确保这是你想要的行为。
使用 Object.values() 方法
如果你只对对象的值感兴趣,也可以使用 Object.values() 方法,它返回对象自身的所有可枚举属性值。
const object = { a: 1, b: 2, c: 3 };
Object.values(object).forEach((value) => {
console.log(value);
});
以上方法中,Object.keys()、Object.entries() 和 Object.values() 是相对较新的 ECMAScript 2017(ES8)方法,而 for...in 循环和 Object.getOwnPropertyNames() 在早期版本的 ECMAScript 中就可用。在使用这些较新的方法时,请确保你的环境支持它们。
es6新语法 rest参数
es6之前 和这个参数类似的用法是用argument
, argument也是用来接受多余传参的,只不过argument是一个伪数组形式接受,而rest会将多余参数以真数组形式返回
使用方法是写在形参最后一个,前面加上3个.
function f(a,b,argument,...rest) {
console.log(a,b,rest)
//1 2 [ 5, 6, 7 ]
}
f(1,2,4,5,6,7)
es6新语法 扩展运算符的使用和场景
以前遍历输出数组或者对象,需要for循环,但是新语法支持使用...数组 或者 ...对象进行遍历输出
let a = [1,2,3,4]
let b = {
"name":"alex",
"age":18
}
console.log(7,8,...a) // 扩展符...a 7 8 1 2 3 4
console.log({"home":"北京",...b}) //扩展符...b { home: '北京', name: 'alex', age: 18 }
应用场景
可以考虑把数组或者对象作为形参参入,并和形参一一对应.
实现这个有2种用法,一个是数组的解构赋值,另一个就可以用扩展运算符
let arr = [1,2,3]
function f1([a,b,c]) {
//使用了数组解构赋值法
console.log(a,b,c)
}
function f2 (a,b,c) {
console.log(a,b,c)
}
f1(arr)
f2(...arr) ////使用了扩展运算符
第二个应用场景是实现数组合并
//数组合并
let brr = [1,2,3]
let crr = [1,2,3]
let newarr=[...brr,...crr]
console.log(newarr)
第三个应用场景是实现对象合并
let b = {
"name":"alex",
"age":18
}
let c = {
"location":{
"south":["深圳","广东"],
"east":["浙江","上海"]
},
"age":40
}
let d = {...b,...c}
console.log(d)
//当然对象原生的assign方法也可以实现对象的合并
let e = Object.assign({},b,c)
console.log(e)
第4个使用扩展运算符完成解构赋值
let arr = [1,2,3,4,5,6,7];
let [a,b,...newarr]=arr;
console.log(newarr); //[3,4,5,6,7]
es6新语法 箭头函数
-
固定语法
(参数)=>{函数体}
注意点:
-
形参个数如果为1个,小括号可省略,没有参数,或者大于2个参数,不能省略
-
函数体里如果只有一个语句,可以省略大括号,反之则不能省略,但是不推荐使用简写
-
如果返回的是一个对象,那么大括号和return不能省略
-
箭头函数不能用arguments获取参数列表,但是可以用
...rest
代码示例
var t =name=>console.log(name.toUpperCase());
-
-
应用场景
对于需要把回调函数作为参数传入的,通常使用箭头函数
setTimeout( ()=>{ console.log("回调函数会常使用箭头函数") },1000);
箭头函数自身没有作用域,因此在箭头函数内返回的是外一层的作用域.这个特性也经常被利用.看下方代码
<script> var bg = document.getElementById("bd"); //普通的函数是有作用域的,这里的this输出的是window对象,因为setTimeout是一个windows的BOM对象 bg.onclick=function () { setTimeout(function () { console.log(this) },1000); //箭头函数没有作用域,都是指向外一层的作用域,因此这里的this指向的不是setTimeout,而是bg. // 这个特性可以被拿来利用减少代码量,直接获取DOM对象 bg.onclick=function () { setTimeout(()=> { console.log(this) },1000) }; </script>
PROMISE
- JS都是默认支持异步的.假设有三个动作,洗菜,吃饭,洗碗,这三个程序都是异步执行,但是彼此之间却有着执行的先后顺序,那么用原生或者ajax实现会出现地狱回调的现象.ajax会有多层嵌套,修改或者查看很麻烦,代码实例如下:
<script type="text/javascript" src="../jQuery/jquery-3.5.1.js"></script>
<script type="text/javascript">
$(function () {
$("div").click(function () {
console.log("点击开始");
$.ajax({
url: "http://kumanxuan1.f3322.net:8809/index/banners/travel",
type: "GET",
success(res) {
if (res.code === 200) {
console.log("第一个网址请求成功");
$.ajax({
url: "http://kumanxuan1.f3322.net:8809/strategies/themes",
type: "GET",
success(res) {
if (res.code === 200) {
console.log("第二个网址请求成功");
$.ajax({
url: "http://kumanxuan1.f3322.net:8809/travels/query",
type: "GET",
success(res) {
if (res.code === 200) {
console.log("第三个网址请求成功");
console.log(res.data)
}
}
})
}
}
})
}
}
})
})
})
</script>
-
先看下promise的基本使用语法
对象状态不受外界影响,Promise代表一个异步操作,有三种状态:pending,fulfilled,rejected.
pending(进行中),fulfilled(已成功)和rejected(已失败),只有异步操作的结果可以决定当前是哪种状态,其他操作无法改变.
pending状态一旦改变,就不会再变,任何时候都可以得到这个结果,而pending一旦变成fulfilled或者rejected就会一直保持这个结果.(1)使用new实例化一个Promise对象,Promise的构造函数中传递一个参数。这个参数是一个函数,该函数用于处理异步任务。
(2)并且传入两个参数:resolve和reject,分别表示异步执行成功后的回调函数和异步执行失败后的回调函数;
(3)通过 promise.then() 处理返回结果。这里的 p 指的是 Promise实例。
let flag = true; //默认状态是pending let promis_ajax = new Promise((resolve,reject)=>{ if (flag){ resolve("洗菜") //如果成功状态从pending =>fulfilled resolve是异步操作成功后的回调函数 } else{ reject("洗菜失败") //如果失败状态从pending =>reject reject是异步操作失败后的回调函数 } }); promis_ajax.then((data)=>{ console.log("成功了,开始"+data) }).catch(error=>{ console.log("我去,"+error) })
-
接下来我们用promise改一下之前的ajax请求,把三个异步动作串联起来,只有完成第一步,才能触发下一步代码
let p1 = new Promise((resolve, reject) => { $.ajax({ url: "http://kumanxuan1.f3322.net:8809/index/banners/travel", // url: "https://fishc.com.cn/forum-43-1.html", type: "GET", success(res) { resolve(res) }, error(err) { reject(err) } }) }); let p2 = new Promise((resolve, reject) => { $.ajax({ url: "http://kumanxuan1.f3322.net:8809/strategies/themes", // url: "https://fishc.com.cn/forum-43-1.html", type: "GET", success(res) { resolve(res) }, error(err) { reject(err) } }) }); let p3 = new Promise((resolve, reject) => { $.ajax({ url: "http://kumanxuan1.f3322.net:8809/travels/query", type: "GET", success(res) { resolve(res) }, error(err) { reject(err) } }) }); //首先生成3个promise的实例对象,在Promise中把ajax写进去,让ajax的success结果执行resolve,error结果执行reject方法 p1.then((res) => { console.log("成功了"); console.log(res); return p2 //重点在这里,返回的是p2,用来改变下一个then的事件指向 }).then((res) => { //因为上面p1的then返回了p2对象,因此p1执行成功后会对象从p1改成p2,然后继续p2的成功语句 console.log("第二个请求成功"); console.log(res); return p3 }).then((res) => { //因为上面p2的then返回了p3对象,因此p2执行成功后会对象从p2改成p3,然后继续p3的成功语句 console.log("第三个请求成功"); console.log(res); }) //------------------对上面代码进一步封装------------------------------------------ //上面的生成实例方法可以进一步封装,提高代码复用 function ajax_pro(url){ return new Promise((resolve,reject)=>{ $.ajax({ url: url, type: "GET", success(res) { resolve(res) }, error(err) { reject(err) } }) }) } let p1 = ajax_pro("http://kumanxuan1.f3322.net:8809/index/banners/travel"); let p2 = ajax_pro("http://kumanxuan1.f3322.net:8809/strategies/themes"); let p3 = ajax_pro("http://kumanxuan1.f3322.net:8809/travels/query"); p1.then((res) => { console.log("成功了"); console.log(res); return p2 //注意这里,返回的是p2,用来改变下一个then的事件指向 }).then((res) => { //因为上面p1的then返回了p2对象,因此p1执行成功后会对象从p1改成p2,然后继续p2的成功语句 console.log("第二个请求成功"); console.log(res); return p3 }).then((res) => { //因为上面p1的then返回了p2对象,因此p1执行成功后会对象从p1改成p2,然后继续p2的成功语句 console.log("第三个请求成功"); console.log(res); });
下面是promise和原生xhr封装模拟ajax的代码
//传入setting一个对象,里面包含url,method和data
function ajax_promise(setting) {
return new Promise((resolve, reject) => {
let method = setting.method || "GET";
let url = setting.url;
let data = setting.data || "";
//实例化一个原生ajax对象
let xhr = new XMLHttpRequest();
if (method === "GET") {
xhr.open(method, url + "?" + data); //get使用url传参
xhr.send()
} else if (method === "POST") {
xhr.open(method, url);
//POST模式下需要设置请求头
xhr.setRequestHeader("Content-type", "application/json");
xhr.send(data)
}
//监听函数
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText)
resolve(xhr.responseText)
}
};
xhr.onerror = (err) => {
reject(err)
}
})
}
let setting = {
url: "http://kumanxuan1.f3322.net:8809/index/banners/travel",
method: "GET",
};
ajax_promise(setting).then(res => {
console.log(`成功了,返回的数据是${res.toString()}`
});
自执行函数的几种写法
自执行函数是很自私的,它的内部可以访问全局变量。但是除了自执行函数自身内部,是无法访问它的。
自执行函数有三种写法:
( function ( “ 参数 ” ) { " 函数方法 " ; } ) ( “ 给参数传的值 ” )
( function ( " 参数 " ) { “ 函数方法 ” ; } ( " 给参数传的值 " ) )
! function ( " 参数 " ) { " 函数方法 " ; } ( " 给参数传的值 " )
(function aaa(a,b){
return sum = a + b
;})(1,2)
自执行函数也可以自行创建对象实例,并调用对象方法
function Dog(name, gender) {
this.name = name;
this.gender = gender;
}
Dog.prototype.action = function () {
console.log("wangwang")
};
//自执行生成实例对象,并调用action方法
(new Dog("111","2222")).action();
正常生成实例对象是
let d =new Dog("111","222")
上面这段代码和下面效果一样
(new Dog("111","2222"))
构造函数
以下是js的构造函数创建对象的示例,但是构造函数中action指向了一个方法,这种写法会导致每生成一个对象实例,是一个错误的写法
function Dog(name,gender,action) {
this.name = name;
this.gender = gender;
this.action = function () {
console.log("wangwang")
}
}
//创建实例对象
let d = new Dog("wang","boy");
let d2 = new Dog("wang2","girl");
//它的action方法都会开辟一块新的内存空间,因此d.action === d2.action 他们不相等,因为他们被引用的内存地址完全不同
console.log(d.action === d2.action); //false
构造对象正确写法
构造函数中上面的name,gender属性直接构造函数中写,但是方法要用下面这种原型prototype创建,这样可以复用一个内存地址,避免内存浪费现象.
function Dog(name,gender,action) {
this.name = name;
this.gender = gender;
}
//用原型创建方法
Dog.prototype.action2 = function () {
console.log("wangwang")
};
console.log(d.action2 === d2.action2); //true
构造函数之原型
那么什么是原型(prototype)呢,原型其实也是构造函数中的一个属性, 虽然他构造函数的属性,但是它自身却是一个对象
他在我们创建对象(let d = new)的时候自动分配,作用是给构造函数的实例对象提供方法
从上所知,原型(prototype)是对象的一个属性,从对象可以调用到原型,那么从原型是否可以关联到对象呢?
原型中有个constructor属性,存放的就是对象自己. 所以结论如下:
对象 通过原型(prototype) 获取对象方法
原型(prototype) 通过constructor 获取对象自身
console.log(d.constructor ===Dog); //true,
可以确认实例对象的constructor 就是对象本身
构造函数原形之Prototype和Constructor关系讲解
Prototype和Constructor:Person类的Prototype就是Person类的原形,原形自身也是对象,只是这里作为Person类的一个属性存在.
原形的Constructor指向的是Person类,而实例与原形之间是用__proto__指向.
构造函数之用原型封装代码 ===> 手动写Jquery
<script type="text/javascript">
window.onload = function () {
function Jquery(selector) {
//1.1 用原生方法获取dom里元素的数组,但是注意这是一个Nodelist的对象的伪数组
let sec = document.querySelectorAll(selector);
//1.2 把Nodelist的对象的伪数组元素写入到this中去
for (let i = 0; i < sec.length; i++) {
this[i] = sec[i] // 这段代码却可以写入
}
this.length = sec.length;
console.log(this)
}
Jquery.prototype.on = function (type, callback) {
//jQuery的on方法调用是 $("div").on(click,function(){} ) 原生绑定事件是用addEventListener
//2.1 用原生的 addEventListener 注册事件
for (let i = 0; i < this.length; i++) {
console.log(this[i]);
this[i].addEventListener(type, callback)
}
};
Jquery.prototype.css = function (prop, value) {
//3.2 封装的jquery修改css可以传入一个值 key,value形式,也可以传入一个对象{key:value,key2:value2}
if (typeof prop === "object") {
for (let key in prop ){
for (i = 0; i < this.length; i++) {
this[i].style[key] = prop[key];
}
}
}else{
for (i = 0; i < this.length; i++) {
//3.1 封装css方法
//注意,原生修改css属性是document.getelementbyid().style.color = red
//但是我们构造方法时候如果用 this[i].style.prop = value会报错,因为这里他会去寻找style下的prop属性,实际上没有这个属性
//要把prop作为形参传入需要用字典的 obj[key]=value 模式
this[i].style[prop] = value;
}
}
// 3.3 return是为了实现链式编程 比如这种调用模式 $('div').css("backgroundColor","yellow").css("width","300px")
return this
};
//这个我们调用jQuery的原理就是这么封装的,可以复用生成实例的这么一个代码
//相当于把 let jq = new Jquery(selector) 给封装了 可以用$('div') 进行调用
function $(selector) {
return new Jquery(selector)
}
$('div').css("backgroundColor","yellow").css("width","300px")
$(".d").on("click",function () {
console.log(this)
})
}
</script>
构造函数之间的继承
继承也是沿用属性在类中定义,方法去原形中创建的规则
代码展示如下:
//父类Person 构造函数中定义属性
function Person(name,age) {
this.name = "alex"
this.age = 18
gender = "man"
}
//父类原形中定义sayhi方法
Person.prototype.sayhi = function (ss) {
console.log(ss)
}
//子类Student 继承父类的name,age属性,自定义teacher属性
function Student(name,age,teacher) {
//继承父类属性
Person.call(this,name,age)
//子类Student 自定义teacher属性
this.teacher = teacher
}
Student.prototype = new Person()
s = new Student("ergou",18,"Mr wang")
// console.log(s)
//父类person的方法都是定义在爷爷类的原形上
s.sayhi("dadada")
注意上面代码这里Student.prototype = new Person()
关系图如下,把子类用prototye属性连接到父类实例,补全类的构造关系,此时父类实例就等同于Student类的原形.
但是此时此刻关系还没完全不全,因为Student原形和Student类之间的Constructor关系还是缺失的,需要补全,否则我们只能继承父类的方法,但是Student原形无法自定义方法
我们在Student.prototype = new Person()
后补一句Student.prototype.constructor = Student
代码如下:
//父类Person 构造函数中定义属性
function Person(name,age) {
this.name = "alex"
this.age = 18
gender = "man"
}
//父类原形中定义sayhi方法
Person.prototype.sayhi = function (ss) {
console.log(ss)
}
//子类Student 继承父类的name,age属性,自定义teacher属性
function Student(name,age,teacher) {
//继承父类属性
Person.call(this,name,age)
//子类Student 自定义teacher属性
this.teacher = teacher
}
//原型继承步骤
//1. 补全子类的原型关系
Student.prototype = new Person()
//2.补全原型和类的constructor关系
Student.prototype.constructor = Student
//3.给子类的原型上加方法
Student.prototype.sayhi = function (ss) {
console.log(`from Student类,形参是${ss}`)
}
s = new Student("ergou",18,"Mr wang")
// console.log(s)
//父类person的方法都是定义在爷爷类的原形上
s.sayhi("dadada")
此时父类和子类的关系图如下:
总结父类继承的步骤
- 子类继承父类属性,并自定义子类属性
function Student(name,age,teacher) {
//继承父类属性
Person.call(this,name,age)
//子类Student 自定义teacher属性
this.teacher = teacher
}
- 补全子类的原型关系后,把子类的方法定义到原型上
//1. 补全子类的原型关系
Student.prototype = new Person()
//2.补全原型和类的constructor关系
Student.prototype.constructor = Student
//3.给子类的原型上加方法
Student.prototype.sayhi = function (ss) {
console.log(`from Student类,形参是${ss}`)
}
ES6语法中的构造函数以及继承使用
上面的构造和继承都是ES5的语法,ES6语法有更简单便捷的,上面是一个类的语法书写,下面是继承使用
class 类名{
constructor(属性值) {
this.属性名 = 属性值
}
方法名(参数){
方法体
}
}
//--------------分割线-------------------
class Person{
constructor(name) {
this.hisname = name
}
sayhi(s){
console.log(`from ${this.hisname} ,她说 ${s}`)
}
}
//1. 子类 extends 父类
class Stu extends Person{
constructor(name,age) {
//2 继承父类属性值
super(name);
this.age = age
}
sayhi(s) {
console.log("from stu sayhi")
}
}
s = new Stu("blex",19)
s.sayhi("from s")
一个生成多个球在画布中来回弹跳的实例练习
window.onload=function () {
/**
*
*/
class Ball{
/**
* 圆球的类
* @param x x轴的位置
* @param y y轴的位置
* @param vx x轴移动步长
* @param vy y轴移动步长
* @param radius 圆球半径
* @param color 圆球颜色
*/
constructor(x,y,vx,vy,radius,color) {
this.x=x;
this.y=y;
this.vx=vx;
this.vy=vy;
this.radius=radius;
this.color=color;
}
move(){
this.x+=this.vx;
this.y+=this.vy;
//下面代码实现圆球到边缘反弹效果,拿x轴举例,圆球在最左的距离为他的半径,最右边局里为画布宽度减去圆半径
if (this.x<=this.radius||this.x>=(800-this.radius)){
this.vx*=-1
}
if (this.y<=this.radius||this.y>=(600-this.radius)){
this.vy*=-1
}
}
draw(con){
// con.fillStyle="rgba(0,0,0,.1)"
// con.fillRect(0, 0, 800, 600);
con.beginPath();
con.arc(this.x,this.y,this.radius,0,Math.PI*2);
con.fillStyle=this.color;
con.closePath();
con.fill()
}
}
function random(min,max){
//生成随机数
return Math.floor(Math.random()*(max-min)+min)
}
function random_color(){
//生成随机颜色
return `rgb(${random(0,255)},${random(0,255)},${random(0,255)})`
}
let cvs=document.getElementById("cvs");
//生成一张画布
let con = cvs.getContext("2d");
//生成20个ball实例对象
let balls=[];
for (i=0;i<20;i++){
ball = new Ball(random(100,700),random(100,500),random(-5,10),random(-5,10),2*Math.PI,random_color())
balls.push(ball)
}
setInterval(()=>{
//画布填充黑色,透明度0.4
con.fillStyle="rgba(0,0,0,.4)";
//颜色填充范围
con.fillRect(0, 0, 800, 600);
//遍历20个实例对象,调用draw和move方法,实现移动
balls.forEach(ball=>{
ball.draw(con);
ball.move()
})
},30)
};
常用第三方模块
aes加密模块
安装命令 npm install crypto-js
/ 引入 crypto-js 库
const CryptoJS = require('crypto-js');
/**
* 使用 AES-256 加密数据
*
* @param {string} message - 要加密的明文数据
* @param {string} key - 用于加密的密钥 (256 位)
* @param {string} iv - 初始化向量 (128 位)
* @returns {string} 加密后的密文 (Base64 编码)
*/
function encrypt(message, key, iv) {
// 将密钥和 IV 转换为 WordArray 对象
const keyObj = CryptoJS.enc.Utf8.parse(key);
const ivObj = CryptoJS.enc.Utf8.parse(iv);
// 使用 AES-256 进行加密
const encrypted = CryptoJS.AES.encrypt(message, keyObj, {
iv: ivObj,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 返回 Base64 编码的密文
return encrypted.toString();
}
/**
* 使用 AES-256 解密数据
*
* @param {string} ciphertext - 要解密的密文 (Base64 编码)
* @param {string} key - 用于解密的密钥 (256 位)
* @param {string} iv - 初始化向量 (128 位)
* @returns {string} 解密后的明文数据
*/
function decrypt(ciphertext, key, iv) {
// 将密钥和 IV 转换为 WordArray 对象
const keyObj = CryptoJS.enc.Utf8.parse(key);
const ivObj = CryptoJS.enc.Utf8.parse(iv);
// 使用 AES-256 进行解密
const decrypted = CryptoJS.AES.decrypt(ciphertext, keyObj, {
iv: ivObj,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 返回解密后的明文数据
return decrypted.toString(CryptoJS.enc.Utf8);
}
// 示例用法
const message = 'This is a secret message.';
const key = 'YourSecretKey1234YourSecretKey1234'; // 256 位密钥
const iv = 'YourIV123456789012'; // 128 位 IV
// 加密
const ciphertext = encrypt(message, key, iv);
console.log('Ciphertext:', ciphertext);
// 解密
const plaintext = decrypt(ciphertext, key, iv);
console.log('Plaintext:', plaintext);
rsa加密模块
安装命令 npm install jsrsasign
// 引入 jsrsasign 库
const jsrsasign = require('jsrsasign');
/**
* 生成 RSA 密钥对
*
* @param {number} bitLength - 密钥长度 (例如:2048)
* @returns {{publicKey: string, privateKey: string}} 包含公钥和私钥的对象
*/
function generateRSAKeyPair(bitLength = 2048) {
const keypair = jsrsasign.KEYUTIL.generateKeypair("RSA", bitLength);
return {
publicKey: jsrsasign.KEYUTIL.getPEM(keypair.pubKeyObj),
privateKey: jsrsasign.KEYUTIL.getPEM(keypair.prvKeyObj),
};
}
/**
* 使用 RSA 公钥加密数据
*
* @param {string} plaintext - 要加密的明文数据
* @param {string} publicKey - RSA 公钥 (PEM 格式)
* @returns {string} 加密后的密文 (Base64 编码)
*/
function encryptRSA(plaintext, publicKey) {
const rsa = new jsrsasign.RSAKey();
rsa.readPrivateKeyFromPEM(publicKey); // 使用公钥加密
const encrypted = rsa.encrypt(plaintext);
return jsrsasign.hextob64(encrypted); // 将十六进制密文转换为 Base64
}
/**
* 使用 RSA 私钥解密数据
*
* @param {string} ciphertext - 要解密的密文 (Base64 编码)
* @param {string} privateKey - RSA 私钥 (PEM 格式)
* @returns {string} 解密后的明文数据
*/
function decryptRSA(ciphertext, privateKey) {
const rsa = new jsrsasign.RSAKey();
rsa.readPrivateKeyFromPEM(privateKey);
const decryptedHex = rsa.decrypt(jsrsasign.b64tohex(ciphertext)); // 将 Base64 密文转换为十六进制
return jsrsasign.hextoutf8(decryptedHex); // 将十六进制解密结果转换为 UTF-8 字符串
}
// 示例用法
const message = 'This is a secret message.';
// 生成 RSA 密钥对
const { publicKey, privateKey } = generateRSAKeyPair();
console.log("Public Key:", publicKey);
console.log("Private Key:", privateKey);
// 使用公钥加密
const ciphertext = encryptRSA(message, publicKey);
console.log('Ciphertext:', ciphertext);
// 使用私钥解密
const plaintext = decryptRSA(ciphertext, privateKey);
console.log('Plaintext:', plaintext);