先解答一下为什么微信开发者工具可以,但是手机上不行。这里踩坑了很久。一直以为是代码的问题,反复调试。最后解决了。
原因:直接点击打开h5网站链接导致
解决:
1、把链接加入公众号的菜单,从公众号进入h5网站
2、将链接生成二维码,扫码进入网站等等
通过上面的两种方式进入网站后再去分享就正常了
h5自定义卡片分享
直接分享url链接是最简单的方式,但是直接分享链接可信度不高,现在人们看到url链接都有一定的防范意识,以为是诱导、诈骗等链接。可信度低,一般不会点开,所以我们需要自定义卡片分享的形式
我这里用的是uniapp,直接上代码
第一步
1、申请一个服务号。并且必须是认证通过的服务号
2、填写ip白名单
3、将域名加入JS安全域名中(分享时分享的链接必须在JS安全域名中)
第二步
前端开发
1、安装weixin-js-sdk的依赖
npm install weixin-js-sdk --save
2、引入安装好的依赖
import wx from "weixin-js-sdk";
3、代码实现
    // 分享功能
    share() {
      // 获取当前页面的完整URL(去除hash部分)
      const currentUrl = window.location.href.split('#')[0];
        // 请求后端获取配置
      http.get('/Share/buildConfig', {url: currentUrl}).then((res) => {
        if (res.code == 0) {
          let wxSign = res.data;
          console.log('微信配置参数:', wxSign);
          wx.config({
            debug: true, // 调试模式,生产环境改为false
            appId: wxSign.appId,
            timestamp: wxSign.timestamp,
            nonceStr: wxSign.nonceStr,
            signature: wxSign.signature,
            jsApiList: [
              'updateAppMessageShareData',
              'updateTimelineShareData'
            ], // 添加更多接口以备后用
            openTagList: [] // 如果需要使用开放标签
          });
          // 在ready回调中设置分享
          wx.ready(() => {
            console.log('微信JS-SDK初始化成功');
            this.setShareData('我是标题', '我是描述', 'http://xxx.com/static/icon/logo.jpg');
          });
          wx.error((err) => {
            console.error('微信JS-SDK初始化失败:', err);
            // 可以在这里尝试重新获取签名
          });
        }
      }).catch(error => {
        console.error('获取签名失败:', error);
      });
    },
    // 设置分享参数
    setShareData(title, desc, imgUrl) {
      const currentUrl = location.href.split('#')[0];
      const shareData = {
        title: title,
        desc: desc,
        link: currentUrl, // 使用当前页面URL,域名必须在公众号安全域名中
        imgUrl: imgUrl, // 必须是完整URL
        success: () => {
          console.log('分享设置成功');
          // 可以在这里添加分享成功的数据统计
        },
        cancel: () => {
          console.log('用户取消分享');
        },
        fail: (err) => {
          console.error('分享设置失败:', err);
        }
      };
      try {
        // 新的API(推荐)
        wx.updateAppMessageShareData(shareData);
        wx.updateTimelineShareData(shareData);
        // 兼容旧API(备用)
        // wx.onMenuShareAppMessage(shareData);
        // wx.onMenuShareTimeline({
        //   title: title,
        //   link: currentUrl,
        //   imgUrl: imgUrl,
        //   success: shareData.success,
        //   cancel: shareData.cancel
        // });
        // wx.onMenuShareQQ(shareData);
        // wx.onMenuShareQZone(shareData);
      } catch (error) {
        console.error('设置分享失败:', error);
      }
    }
第三步
后端实现
我这里使用的是thinkphp 的 EasyWechat
安装EasyWechat,具体操作看文档:
https://easywechat.com/4.x/installation.html
 composer require overtrue/wechat:~4.0 -vvv
php接口
    // 获取分享配置
    public function buildConfig()
    {
        $url = $this->request->param("url");
        $arr = [
            'updateAppMessageShareData',
            'updateTimelineShareData'
        ];
        $data = (new Wechat())->buildConfig($arr, $url);
        return $this->success("成功", json_decode($data, true));
    }
<?php
namespace app\common\service;
use EasyWeChat\Factory;
class Wechat
{
     //客户端配置信息
    public $config = [
        'app_id'        => 'xxxxx', // 填写自己的公众号appid
        'secret'        => 'xxxxxxx',// 填写自己的公众号秘钥
        // 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名
        'response_type' => 'array',
        //...
    ];
    public function buildConfig($APIs, $url){
        $app = Factory::officialAccount($this->config);
        $app->jssdk->setUrl($url);
        return $app->jssdk->buildConfig($APIs, $debug = false, $beta = false, $json = true);
    }
}
完成!
java springboot 项目允许跨域访问
package com.test.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        System.out.println("=======================允许跨域访问==============");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }
}
使用SpringBoot开发的时候,录入数据里面包含了事件字段,可能会出现这样的问题,直接打开数据表查看事件,会发现数据表中存储的事件会比录入的时间少8个小时,
少八个时区很容易想到,我们处于东八区,java存储的时间默认是以0时区为准,默认情况下,会自动转换成0时区的时间
方法一:
在实体类中,标注时间的时区timezone
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
该方法必须每个字段都设置,很不方便
方法二
在数据库配置中设置时区
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/数据库名称?serverTimezone=Asia/Shanghai
推荐使用
大数字显示的时候,为了方便阅读,需要对数字用千分号分割
格式化数字
1、使用各国语言显示数字
2、千分号格式化数字
3、添加货币符号
4、添加百分号
var num = 12345678
num.toLocaleString() // "12,345,678"
添加货币符号
var num = 1234
num.toLocaleString('zh',{style:'currency' , currency:'CNY' }) // "¥1,234"
添加百分号
var num = 1234
num.toLocaleString('zh',{style:'percent'}) // "¥1,234"
思路:
1.取第一个数组的第一个元素与后面的元素作比较,找出公共的前缀
2.将该前缀作为第一个元素,继续与后面的元素作比较,找出公共的前缀
遍历字符串,找出相同的前缀
代码:
    var longestCommonPrefix = function(strs) {
        // 判断字符串是否为空
        if(strs == null || strs.length===0){
            return “”
        }
        let result = strs[0];
        // 从数组的二个元素开始遍历
        for(let i = 1;i < strs.length;i++){
            let len = Math.min(result.length, strs[i].length);
        let index = 0;
        while(index < len && result.charAt(index) == strs[i].charAt(index)){
          index++;
        }
        if(index == 0){
          return “”
        }
        result = result.substr(0,index);
      }
      return result;
    };
解题思路:使用栈和遍历
栈:后进先出
示例代码:
//  有效的括号:括号要左右配对顺序相同,相当于栈
// 字符串长度必须是偶数
//  1.遍历字符串
//  2.遇到左括号依次入栈,根据先进后出,栈顶即为最里面的左括号
//  3.遇到右括号,判断此时栈顶元素是否和Map里右括号对应的值相同,且栈不为空
//  4.若栈顶元素与右括号对应的值不匹配,则返回false
//  5.否则出栈
//  6.最后若栈为空,则是有效字符串,返回true
    var isValid = function(s) {
        const len = s.length;
        if(len % 2 !=0){
            return false;
        }
        const pairs =new Map([
            [‘)’,’(‘],
            [‘}’,’{‘],
            [‘]’,’[‘],
        ])
        let stk = [];
        for(let ch of s){
            if(pairs.has(ch)){
                if(!stk.length || stk[stk.length-1] != pairs.get(ch)){
                    return false;
                }
                stk.pop();
            }else{
                stk.push(ch);
            }
        }
        return !stk.length;
    };
解题思路:
1.使用双指针法的快慢指针,定义slow和fast做为指针。
2.初始化指针slow指向数组的起始位置(nums[0]),指针fast指向指针slow的后一个位置(nums[1])。
3.指针fast不断向后移动,将指针fast指向的元素与指针slow指向的元素进行比较。
代码:
    var removeDuplicates = function(nums) {
        if(nums.length == 0){
            return 0;
        }
        // 初始化时指针slow指向数组的起始位置(nums[0]),指针fast指向指针slow的后一个位置(nums[1])。
        let slow = 0,fast = 1;
            while(fast<nums.length){
                if(nums[fast]!=nums[slow]){
                    slow = slow + 1;
                    nums[slow] = nums[slow];
                }
                fast = fast + 1;
            }
            return slow + 1;
    };
解题思路:
1.使用双指针法的快慢指针,定义slow和fast做为指针。
2.快指针:寻找新数组元素,新数组就是不含有目标元素的数组
  慢指针:指向更新新数组下标的位置
代码:
    var removeElement = function(nums, val) {
        if(nums.length == 0){
            return 0;
        }
        var slow = 0;
        var fast;
        for(fast = 0;fast<nums.length; fast++){
            if(nums[fast] == val){
                continue;
            }else{
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    };
解题思路:
1.利用二分法查找左边边界。
代码:
    var searchInsert = function(nums, target) {
        let left = 0;
        right = nums.length - 1;
        ans = nums.length;
        while(left<=right){
            let mid = parseInt((right + left)/2);
            if(target <= nums[mid]){
                ans = mid;
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        return ans;
};
思路:
1.取第一个数组的第一个元素与后面的元素作比较,找出公共的前缀
2.将该前缀作为第一个元素,继续与后面的元素作比较,找出公共的前缀
遍历字符串,找出相同的前缀
代码:
    var longestCommonPrefix = function(strs) {
        // 判断字符串是否为空
        if(strs == null || strs.length===0){
            return “”
        }
        let result = strs[0];
        // 从数组的二个元素开始遍历
        for(let i = 1;i < strs.length;i++){
            let len = Math.min(result.length, strs[i].length);
        let index = 0;
        while(index < len && result.charAt(index) == strs[i].charAt(index)){
          index++;
        }
        if(index == 0){
          return “”
        }
        result = result.substr(0,index);
      }
      return result;
    };
解题思路:使用栈和遍历
栈:后进先出
示例代码:
//  有效的括号:括号要左右配对顺序相同,相当于栈
// 字符串长度必须是偶数
//  1.遍历字符串
//  2.遇到左括号依次入栈,根据先进后出,栈顶即为最里面的左括号
//  3.遇到右括号,判断此时栈顶元素是否和Map里右括号对应的值相同,且栈不为空
//  4.若栈顶元素与右括号对应的值不匹配,则返回false
//  5.否则出栈
//  6.最后若栈为空,则是有效字符串,返回true
    var isValid = function(s) {
        const len = s.length;
        if(len % 2 !=0){
            return false;
        }
        const pairs =new Map([
            [‘)’,’(‘],
            [‘}’,’{‘],
            [‘]’,’[‘],
        ])
        let stk = [];
        for(let ch of s){
            if(pairs.has(ch)){
                if(!stk.length || stk[stk.length-1] != pairs.get(ch)){
                    return false;
                }
                stk.pop();
            }else{
                stk.push(ch);
            }
        }
        return !stk.length;
    };
解题思路:
1.使用双指针法的快慢指针,定义slow和fast做为指针。
2.初始化指针slow指向数组的起始位置(nums[0]),指针fast指向指针slow的后一个位置(nums[1])。
3.指针fast不断向后移动,将指针fast指向的元素与指针slow指向的元素进行比较。
代码:
    var removeDuplicates = function(nums) {
        if(nums.length == 0){
            return 0;
        }
        // 初始化时指针slow指向数组的起始位置(nums[0]),指针fast指向指针slow的后一个位置(nums[1])。
        let slow = 0,fast = 1;
            while(fast<nums.length){
                if(nums[fast]!=nums[slow]){
                    slow = slow + 1;
                    nums[slow] = nums[slow];
                }
                fast = fast + 1;
            }
            return slow + 1;
    };
解题思路:
1.使用双指针法的快慢指针,定义slow和fast做为指针。
2.快指针:寻找新数组元素,新数组就是不含有目标元素的数组
  慢指针:指向更新新数组下标的位置
代码:
    var removeElement = function(nums, val) {
        if(nums.length == 0){
            return 0;
        }
        var slow = 0;
        var fast;
        for(fast = 0;fast<nums.length; fast++){
            if(nums[fast] == val){
                continue;
            }else{
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    };
解题思路:
1.利用二分法查找左边边界。
代码:
    var searchInsert = function(nums, target) {
        let left = 0;
        right = nums.length - 1;
        ans = nums.length;
        while(left<=right){
            let mid = parseInt((right + left)/2);
            if(target <= nums[mid]){
                ans = mid;
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        return ans;
};
思路:
1.取第一个数组的第一个元素与后面的元素作比较,找出公共的前缀
2.将该前缀作为第一个元素,继续与后面的元素作比较,找出公共的前缀
遍历字符串,找出相同的前缀
代码:
    var longestCommonPrefix = function(strs) {
        // 判断字符串是否为空
        if(strs == null || strs.length===0){
            return “”
        }
        let result = strs[0];
        // 从数组的二个元素开始遍历
        for(let i = 1;i < strs.length;i++){
            let len = Math.min(result.length, strs[i].length);
        let index = 0;
        while(index < len && result.charAt(index) == strs[i].charAt(index)){
          index++;
        }
        if(index == 0){
          return “”
        }
        result = result.substr(0,index);
      }
      return result;
    };
解题思路:使用栈和遍历
栈:后进先出
示例代码:
//  有效的括号:括号要左右配对顺序相同,相当于栈
// 字符串长度必须是偶数
//  1.遍历字符串
//  2.遇到左括号依次入栈,根据先进后出,栈顶即为最里面的左括号
//  3.遇到右括号,判断此时栈顶元素是否和Map里右括号对应的值相同,且栈不为空
//  4.若栈顶元素与右括号对应的值不匹配,则返回false
//  5.否则出栈
//  6.最后若栈为空,则是有效字符串,返回true
    var isValid = function(s) {
        const len = s.length;
        if(len % 2 !=0){
            return false;
        }
        const pairs =new Map([
            [‘)’,’(‘],
            [‘}’,’{‘],
            [‘]’,’[‘],
        ])
        let stk = [];
        for(let ch of s){
            if(pairs.has(ch)){
                if(!stk.length || stk[stk.length-1] != pairs.get(ch)){
                    return false;
                }
                stk.pop();
            }else{
                stk.push(ch);
            }
        }
        return !stk.length;
    };
解题思路:
1.使用双指针法的快慢指针,定义slow和fast做为指针。
2.初始化指针slow指向数组的起始位置(nums[0]),指针fast指向指针slow的后一个位置(nums[1])。
3.指针fast不断向后移动,将指针fast指向的元素与指针slow指向的元素进行比较。
代码:
    var removeDuplicates = function(nums) {
        if(nums.length == 0){
            return 0;
        }
        // 初始化时指针slow指向数组的起始位置(nums[0]),指针fast指向指针slow的后一个位置(nums[1])。
        let slow = 0,fast = 1;
            while(fast<nums.length){
                if(nums[fast]!=nums[slow]){
                    slow = slow + 1;
                    nums[slow] = nums[slow];
                }
                fast = fast + 1;
            }
            return slow + 1;
    };
解题思路:
1.使用双指针法的快慢指针,定义slow和fast做为指针。
2.快指针:寻找新数组元素,新数组就是不含有目标元素的数组
  慢指针:指向更新新数组下标的位置
代码:
    var removeElement = function(nums, val) {
        if(nums.length == 0){
            return 0;
        }
        var slow = 0;
        var fast;
        for(fast = 0;fast<nums.length; fast++){
            if(nums[fast] == val){
                continue;
            }else{
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    };
解题思路:
1.利用二分法查找左边边界。
代码:
    var searchInsert = function(nums, target) {
        let left = 0;
        right = nums.length - 1;
        ans = nums.length;
        while(left<=right){
            let mid = parseInt((right + left)/2);
            if(target <= nums[mid]){
                ans = mid;
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        return ans;
};
代码:
    let num1 = 0;
            let num2 = 0;
            for(let i = 0;i<nums.length;i++){
            num1= nums[i]
            for(let j = i+1;j<nums.length;j++){
            num2= nums[j]
            if(num1 + num2 == target){
            return [i,j]
            }
            }
            }
总结:使用for循环,循环两次,第二次循环把i改为j,j=i+1
解题思路:既然回文数是数字反转以后还和之前的数字一样,那么可以判断反转过的数字是否和之前的一样,
是的话就是回文数,不是的话就不是回文数先把数字转化为字符串,解析成数组反转过以后再转化字符串,
然后把字符串在重新转化为数字。
代码:
    var isPalindrome = function(x) {
      // 数字转字符串
        if(x<0){
            return false
        }
      // 反转字符串
        var x2=parseInt(x.toString().split(‘’).reverse().join(‘’))
      // 判断反转后的字符串是否和原始字符串相等
      return x2 === x ? true : false
    };
    isPalindrome(121)
解题思路:
1.左边的罗马数字 > 右边的罗马数字时,罗马数 == 左边罗马数字对应的阿拉伯数字 + 右边罗马数字对应的阿拉伯数字
2.左边的罗马数字 < 右边的罗马数字时,罗马数 == 左边罗马数字对应的阿拉伯数字的反数即负数 + 右边罗马数字对应的阿拉伯数字
代码:
    var romanToInt = function(s) {
        let obj={
            I:1,
            V:5,
            X:10,
            L:50,
            C:100,
            D:500,
            M:1000,
        }
        let num=0
        for(let i = 0; i < s.length; i++){
            if(obj[s[i]] < obj[s[i+1]]){
                num -= obj[s[i]]
            }else{
                num += obj[s[i]]
            }
        }
        return num
    };
    romanToInt (‘III’)
赋值运算符:-=、+=
x+=y,相当于x=x+y
x-=y,相当于x=x-y
代码:
    let num1 = 0;
            let num2 = 0;
            for(let i = 0;i<nums.length;i++){
            num1= nums[i]
            for(let j = i+1;j<nums.length;j++){
            num2= nums[j]
            if(num1 + num2 == target){
            return [i,j]
            }
            }
            }
总结:使用for循环,循环两次,第二次循环把i改为j,j=i+1
解题思路:既然回文数是数字反转以后还和之前的数字一样,那么可以判断反转过的数字是否和之前的一样,
是的话就是回文数,不是的话就不是回文数先把数字转化为字符串,解析成数组反转过以后再转化字符串,
然后把字符串在重新转化为数字。
代码:
    var isPalindrome = function(x) {
      // 数字转字符串
        if(x<0){
            return false
        }
      // 反转字符串
        var x2=parseInt(x.toString().split(‘’).reverse().join(‘’))
      // 判断反转后的字符串是否和原始字符串相等
      return x2 === x ? true : false
    };
    isPalindrome(121)
解题思路:
1.左边的罗马数字 > 右边的罗马数字时,罗马数 == 左边罗马数字对应的阿拉伯数字 + 右边罗马数字对应的阿拉伯数字
2.左边的罗马数字 < 右边的罗马数字时,罗马数 == 左边罗马数字对应的阿拉伯数字的反数即负数 + 右边罗马数字对应的阿拉伯数字
代码:
    var romanToInt = function(s) {
        let obj={
            I:1,
            V:5,
            X:10,
            L:50,
            C:100,
            D:500,
            M:1000,
        }
        let num=0
        for(let i = 0; i < s.length; i++){
            if(obj[s[i]] < obj[s[i+1]]){
                num -= obj[s[i]]
            }else{
                num += obj[s[i]]
            }
        }
        return num
    };
    romanToInt (‘III’)
赋值运算符:-=、+=
x+=y,相当于x=x+y
x-=y,相当于x=x-y
代码:
    let num1 = 0;
            let num2 = 0;
            for(let i = 0;i<nums.length;i++){
            num1= nums[i]
            for(let j = i+1;j<nums.length;j++){
            num2= nums[j]
            if(num1 + num2 == target){
            return [i,j]
            }
            }
            }
总结:使用for循环,循环两次,第二次循环把i改为j,j=i+1
解题思路:既然回文数是数字反转以后还和之前的数字一样,那么可以判断反转过的数字是否和之前的一样,
是的话就是回文数,不是的话就不是回文数先把数字转化为字符串,解析成数组反转过以后再转化字符串,
然后把字符串在重新转化为数字。
代码:
var isPalindrome = function(x) {
  // 数字转字符串
    if(x<0){
        return false
    }
  // 反转字符串
    var x2=parseInt(x.toString().split(‘’).reverse().join(‘’))
  // 判断反转后的字符串是否和原始字符串相等
  return x2 === x ? true : false
};
isPalindrome(121)
解题思路:
1.左边的罗马数字 > 右边的罗马数字时,罗马数 == 左边罗马数字对应的阿拉伯数字 + 右边罗马数字对应的阿拉伯数字
2.左边的罗马数字 < 右边的罗马数字时,罗马数 == 左边罗马数字对应的阿拉伯数字的反数即负数 + 右边罗马数字对应的阿拉伯数字
代码:
var romanToInt = function(s) {
    let obj={
        I:1,
        V:5,
        X:10,
        L:50,
        C:100,
        D:500,
        M:1000,
    }
    let num=0
    for(let i = 0; i < s.length; i++){
        if(obj[s[i]] < obj[s[i+1]]){
            num -= obj[s[i]]
        }else{
            num += obj[s[i]]
        }
    }
    return num
};
romanToInt (‘III’)
 var str="......"
 var currentIndex=0
 let strShow=""
 for(let i = 0;i < str.length; i ++){
 strShow += "<span id='chat_" + i +"'>" + str[i] + "</span>"
 }
 document.setElementById("content").innerHTML=strShow
 document.setElementById("total").innerHTML=(str.length)
 var userInput=""
 document.onkeydown = function(envent){
     var e = event || windown.event;
     //获取用户输入字符
     let value = e.key
     //和当前的字符匹配是否相等
     userInput = userInput + str[currentIndex]
     //显示用户完整输入
     document.getElementById("userInput").innerHTMl=userInput
     //用户输入字符,字符串改变颜色
     document.getElementById("chat_" + currentIndex).classList.add('red')
     //自增,设置下一个需要匹配的索引
     currentIndex ++
 } 
数字相减
111.00-222.20
// -111.19999999999999
数字相加
1.10+2.20
// 3.3000000000000003
数字相乘
0.3*3
// 0.8999999999999999
数字相除
0.3/3
// 0.09999999999999999
JavaScript中的数字运算遵循IEEE 754标准的浮点数算法,这种算法使用二进制来表示实数,以及对实数进行加、减、乘、除等运算。然而,由于二进制不能准确地表示所有的十进制小数,因此,进行数字运算时可能会出现无限小数。
例如,0.1在二进制中是一个无限循环的小数,用64位浮点数表示时就会出现精度误差。如果对0.1和0.2进行加法运算,期望结果是0.3,但是实际结果可能是0.30000000000000004,这是由于二进制表示的精度问题造成的。
先乘以10000转成整数计算后再除以10000,(这里的10000可以自己根据需要调整)
例如:1.1+2.2
Math.round(1.1 * 10000 + 2.2 * 10000)/10000
// 3.3
使用字符串或数组等数据结构来模拟数学运算中的处理过程
具体来说,当计算两个数字相加时,高精度计算库会将数字的每一位分别相加,并且在需要的时候进行进位操作;当计算乘法时,当前位的计算结果是将当前数字的每一位乘以另一个数对应位的数字后相加得到的。
该方法比较复杂,别人已经写有类似的库,我们可以借鉴
以下是一些常用的第三方库:
BigNumber.js:这是一个功能强大的库,支持高精度整数、小数和有理数运算。它提供了类似于JavaScript内置数字类型的API,例如加、减、乘、除等运算,以及比较、取余、幂等运算等。
decimal.js:这是一个基于JavaScript的高精度计算库,支持任意精度的小数运算。它提供了诸如加、减、乘、除等基本运算,还支持一些高级运算,例如求正弦、余弦、幂运算等。
bignumber.js:这也是一个流行的高精度计算库,支持任意精度的整数和小数计算。它提供了相对较小、内存占用低的API,适用于较小的高精度计算场景。
数字相减
111.00-222.20
// -111.19999999999999
数字相加
1.10+2.20
// 3.3000000000000003
数字相乘
0.3*3
// 0.8999999999999999
数字相除
0.3/3
// 0.09999999999999999
JavaScript中的数字运算遵循IEEE 754标准的浮点数算法,这种算法使用二进制来表示实数,以及对实数进行加、减、乘、除等运算。然而,由于二进制不能准确地表示所有的十进制小数,因此,进行数字运算时可能会出现无限小数。
例如,0.1在二进制中是一个无限循环的小数,用64位浮点数表示时就会出现精度误差。如果对0.1和0.2进行加法运算,期望结果是0.3,但是实际结果可能是0.30000000000000004,这是由于二进制表示的精度问题造成的。
先乘以10000转成整数计算后再除以10000,(这里的10000可以自己根据需要调整)
例如:1.1+2.2
Math.round(1.1 * 10000 + 2.2 * 10000)/10000
// 3.3
使用字符串或数组等数据结构来模拟数学运算中的处理过程
具体来说,当计算两个数字相加时,高精度计算库会将数字的每一位分别相加,并且在需要的时候进行进位操作;当计算乘法时,当前位的计算结果是将当前数字的每一位乘以另一个数对应位的数字后相加得到的。
该方法比较复杂,别人已经写有类似的库,我们可以借鉴
以下是一些常用的第三方库:
BigNumber.js:这是一个功能强大的库,支持高精度整数、小数和有理数运算。它提供了类似于JavaScript内置数字类型的API,例如加、减、乘、除等运算,以及比较、取余、幂等运算等。
decimal.js:这是一个基于JavaScript的高精度计算库,支持任意精度的小数运算。它提供了诸如加、减、乘、除等基本运算,还支持一些高级运算,例如求正弦、余弦、幂运算等。
bignumber.js:这也是一个流行的高精度计算库,支持任意精度的整数和小数计算。它提供了相对较小、内存占用低的API,适用于较小的高精度计算场景。
一些需要预约的场景,一般需要显示最近10天的时间给用户选择
let today = (new Date()).getTime()let nextDay = new Date(today + 86400000)function getDateList(num=15){
    // 获取当前日期
    const currentDate = new Date();
    let list = []
    // 循环生成接下来的 num 天日期
    for (let i = 0; i < num; i++) {
      // 生成日期所需的时间戳
      const timestamp = currentDate.getTime() + i * 24 * 60 * 60 * 1000;
      // 将时间戳转换成日期格式
      const date = new Date(timestamp);
      // 输出日期
      let key = formatDate(date,'MM-dd')
      let value = formatDate(date,'yyyy-MM-dd')
      list.push({key,value})
    }
    return list
}
//格式化时间戳
function formatDate(date, format) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hour = String(date.getHours()).padStart(2, '0');
  const minute = String(date.getMinutes()).padStart(2, '0');
  const second = String(date.getSeconds()).padStart(2, '0');
  let result = format.replace(/yyyy/g, year);
  result = result.replace(/MM/g, month);
  result = result.replace(/dd/g, day);
  result = result.replace(/HH/g, hour);
  result = result.replace(/mm/g, minute);
  result = result.replace(/ss/g, second);
  return result;
}
有时候获取到一个对象,需要把改对象拼接成url请求参数发起get请求
function buildGetParam(paramObj){
    for (const key in paramObj) {
        if(paramObj[key] != ''){
            if(paramObj[key] instanceof String){
                str += key +"="+ paramObj[key] + "&"
                console.log(paramObj[key])
            }else if(paramObj[key] instanceof Array){
                paramObj[key].forEach(item => {
                    str += key +"[]="+ item + "&"
                })
            }
        }
    }
}
Javascript刷新页面的几种方法:
1 history.go(0)
2 location.reload()
3 location=location
4 location.assign(location)
5 document.execCommand(‘Refresh’)
6 window.navigate(location)
7 location.replace(location)
8 document.URL=location.href
变量
 1.1 小驼峰命名法:第一个单词首字母小写,其他单词首字母大写,例如 firstName
 1.2 布尔类型变量应使用 is, has, can 等前缀,例如 isCompleted。
 1.3 由字母、数字、下划线、$符号组成,不能以数字开头,以下是不好的例子123name,#abc
 1.4 变量名区分大小写
 1.5 不能使用保留的关键字、保留字、true、false 和 null 
常量名
 使用全大写字母,单词之间使用下划线分隔,例如 MAX_NUMBER。
函数和方法
 3.1 小驼峰命名法:第一个单词首字母小写,其他单词首字母大写,例如 getName
 3.2 函数和方法都是表示对什么东西进行操作,建议第一个单词是动词,名词在后面,例如 getUserList。
类
 4.1 类名使用大驼峰命名法,每个单词第一个字母大写,例如 Person。
变量名应该避免音译命名,应该使用英文单词或者常用缩写来进行命名。例如使用 load 而不是 lode。
缩写应该当获取远离语境并且广为人知。例如 URL 是广为人知的缩写。
避免使用单字母命名,除非是一个已经被广泛接受的惯例,例如 i 用于循环中所索引的变量命名。
网页可见区域宽:document.body.clientWidth
网页可见区域高:document.body.clientHeight
网页可见区域宽:document.body.offsetWidth (包括边线和滚动条的宽)
网页可见区域高:document.body.offsetHeight(包括边线的宽)
网页正文全文宽:document.body.scrollWidth
网页正文全文高:document.body.scrollHeight
网页被卷去的高:document.body.scrollTop
网页被卷去的左:document.body.scrollLeft
网页正文部分上:window.screenTop
网页正文部分左:window.screenLeft
屏幕分辨率的高:window.screen.height
屏幕分辨率的宽:window.screen.width
屏幕可用工作区高度:window.screen.availHeight
屏幕可用工作区宽度:window.screen.availWidth
你的屏幕设置是 window.screen.colorDepth +" 位彩色"
在二开项目的时候,原有的组件会修改页面的dom,为了监听该dom的变化
DOMSubtreeModified: 在DOM结构发生任何变化的时候。这个事件在其他事件触发后都会触发;
DOMNodeInserted: 当一个节点作为子节点被插入到另一个节点中时触发;
DOMNodeRemoved: 在节点从其父节点中移除时触发;
DOMNodeInsertedIntoDocument: 在一个节点被直接插入文档或通过子树间接插入文档之后触发。这个事件在 DOMNodeInserted 之后触发;
DOMNodeRemovedFromDocument: 在一个节点被直接从文档移除或通过子树间接从文档移除之前触发。这个事件在 DOMNodeRemoved 之后触发;
DOMAttrModified: 节点属性被修改之后触发;
DOMCharacterDataModified: 在文本节点的值发生变化时触发;
1.箭头函数与function定义函数的写法:
//function
function fn(a, b){
    return a + b;
}
//箭头函数
var foo = (a, b)=>{ return a + b };
2.this的指向:
使用function定义的函数,this的指向随着调用环境的变化而变化的,而箭头函数中的this指向是固定不变的,一直指向的是定义函数的环境。
//使用function定义的函数
function foo(){
    console.log(this);
}
var obj = { aa: foo };
foo(); //Window
obj.aa() //obj { aa: foo }
使用function定义的函数中this指向是随着调用环境的变化而变化的
//使用箭头函数定义函数
var foo = () => { console.log(this) };
var obj = { aa:foo };
foo(); //Window
obj.aa(); //Window
明显使用箭头函数的时候,this的指向是没有发生变化的。
3.构造函数
//使用function方法定义构造函数
function Person(name, age){
    this.name = name;
    this.age = age;
}
var lenhart =  new Person(lenhart, 25);
console.log(lenhart); //{name: 'lenhart', age: 25}
//尝试使用箭头函数
var Person = (name, age) =>{
    this.name = name;
    this.age = age;
};
var lenhart = new Person('lenhart', 25); //Uncaught TypeError: Person is not a constructor
    function是可以定义构造函数的,而箭头函数是不行的。
4.变量提升
由于js的内存机制,function的级别最高,而用箭头函数定义函数的时候,需要var(let const定义的时候更不必说)关键词,而var所定义的变量不能得到变量提升,故箭头函数一定要定义于调用之前!
foo(); //123
function foo(){
    console.log('123');
}
arrowFn(); //Uncaught TypeError: arrowFn is not a function
var arrowFn = () => {
    console.log('456');
};
1.获取时间
// 获取指定时间
var time = new Date("2017-11-15 22:22:22");
// 获取当前时间
var time = (new Date()).getTime()
2.时间转时间戳
function timetrans(date){
    var date = new Date(date*1000);//如果date为13位不需要乘1000
    var Y = date.getFullYear() + '-';
    var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';
    var D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' ';
    var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
    var m = (date.getMinutes() <10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
    var s = (date.getSeconds() <10 ? '0' + date.getSeconds() : date.getSeconds());
    return Y+M+D+h+m+s;
}
方法: 数组元素交换位置
function swapArray(arr, index1, index2) {
   arr[index1] = arr.splice(index2, 1, arr[index1])[0];
    return arr;
}
方法:上移,将当前数组index索引与后面一个元素互换位置,向数组后面移动一位
function zIndexUp(arr,index,length){
   if(index+1 != length){
       swapArray(arr, index, index+1);
   }else{
      alert('已经处于置顶,无法上移');
  }
}
}
方法:下移 将当前数组index索引与前面一个元素互换位置,向数组前面移动一位
function zIndexDown(arr,index,length){
   if(index!= 0){
       swapArray(arr, index, index-1);
   }else{
      alert('已经处于置底,无法下移');
  }
}
方法:置顶,即将当前元素移到数组的最后一位
function zIndexTop(arr,index,length){
   if(index+1 != length){
     //首先判断当前元素需要上移几个位置,置底移动到数组的第一位
      var moveNum = length - 1 - index;
      //循环出需要一个一个上移的次数
       for (var i = 0; i<moveNum; i++) {
         swapArray(arr, index, index + 1);
          index++;
     }
   }else{
      alert('已经处于置顶');
  }
}
置底,即将当前元素移到数组的第一位
//
function zIndexBottom(arr,index,length){
   if(index!=0){
      //首先判断当前元素需要上移几个位置,置底移动到数组的第一位
      var moveNum = index - 0;
      //循环出需要一个一个上移的次数
       for (var i = 0; i<moveNum; i++) {
         swapArray(arr, index, index - 1);
          index--;
     }
   }else{
      alert('已经处于置底');
  }
}
方法一:函数
var function_name = "test";
//判断是否是函数名
if(function_name && function_name in window){
window[function_name]("参数");
}
function test(paras) {
    alert(paras);
}
方法二:eval
var fun_name = "fun1";
eval(fun_name+"()");
function fun1(){
    alert("eval执行")
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String);  //结果: ['1', '2', '3', '4', '5', '6', '7', '8', '9']
var a = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
a.map(Number);  //结果:[1, 2, 3, 4, 5, 6, 7, 8, 9]
方法一:使用jQuery的inArray
$.inarray(value,array)
方法二:函数实现inArray
function inArray(value,arr) {
  var i = arr.length;
  while (i--) {
    if (arr[i] === value) {
      return true;
    }
  }
  return false;
}
方法三:给Array增加一个函数contains
contains:包含
Array.prototype.contains = function (value) {
  var i = this.length;
  while (i--) {
    if (this[i] === value) {
      return true;
    }
  }
  return false;
}
方法一:按.号切割字符串,获取最有一个元素
getSuffixName(file_path = ''){
    if(!file_path){
        return '';
    }
    let arr = file_path.split('.');
    return arr.length > 1?arr[arr.length - 1]:'';
}
方法二:按.号切割字符串,获取最有一个元素
getSuffixName(file_path = ''){
    if(!file_path){
        return '';
    }
    return file_path.split('.').pop();
}
方法三:获取最后一个.号位置,截取该位置最后的字符串
getSuffixName(file_path = ''){
    if(!file_path){
        return '';
    }
    return file_path.substring(file_path.lastIndexOf('.') + 1);
}
前端问题:伪类first-child 有时候不起作用
<style>
article:first-child {
    color: red;//不生效
}
</style>
<h1>logo</h1>
<article>article1</article>
<article>article2</article>
<article>article3</article>
改进:
<style>
article:first-child {
    color: red;//不生效
}
</style>
<h1>logo</h1>
<div>
    <article>article1</article>
    <article>article2</article>
    <article>article3</article>
</div>
全部采用小写方式, 以下划线分隔。
示例:my_project_name
全部采用小写方式, 以下划线分隔。
有复数结构时,要采用复数命名法。
示例:scripts, styles, images
命名方法: 小驼峰式命名法
命名规范:前缀为形容词 (函数前缀为动词, 以此来区分函数和变量)
let maxCount = 10;
let tableTitle = '标题';
let setConut = 10; //不要使用动词
命名方法:名词全部大写
命名规范:使用大写字母和下划线来组合命名,下划线用来分割单词。
const MAX_COUNT = 10;
const URL = 'http://www.tooi.cn';
命名方法: 小驼峰式命名法
命名规范: 前缀应该为动词
常用动词约定
| 动词 | 含义 | 
|---|---|
| can | 判断是否可执行某个动作 | 
| has | 判断是否含义某个值 | 
| get | 获取某个值 | 
| set | 设置某个值 | 
| load | 加载某些数据 | 
// 是否可阅读
function canRead() {}
// 获取名称
function getName() {}
命名方法:大写驼峰式命名法,首字母大写。
命名规范:前缀为名称。
class Persion {
  constructor(name) {
   ...
  }
}
let person = new Person('张三');
类的成员包括:
class Person {
  // 私有属性 
  _name: string;
  constructor() { }
  // 公共方法
  getName() {
    return this._name;
  }
  // 公共方法
  setName(name) {
    this._name = name;
  }
}
格式化插件推荐prettier
// 设置标题
setTitle()
/*
* 代码执行到这里后会调用setTitle()函数
* setTitle():设置title的值
*/
setTitle();
6.3 函数 & 方法注释
/**
 * 函数说明
 * @关键字
 **/
https://www.cnblogs.com/Hsong/p/9016950.html
https://blog.csdn.net/Jiang216/article/details/121414732
命名方法: 小驼峰式命名法
命名规范:前缀为形容词 (函数前缀为动词, 以此来区分函数和变量)
let maxCount = 10;
let tableTitle = '标题';
let setConut = 10; //不要使用动词
命名方法:名词全部大写
命名规范:使用大写字母和下划线来组合命名,下划线用来分割单词。
const MAX_COUNT = 10;
const URL = 'http://www.tooi.cn';
命名方法: 小驼峰式命名法
命名规范: 前缀应该为动词
常用动词约定
| 动词 | 含义 | 
|---|---|
| can | 判断是否可执行某个动作 | 
| has | 判断是否含义某个值 | 
| get | 获取某个值 | 
| set | 设置某个值 | 
| load | 加载某些数据 | 
// 是否可阅读
function canRead() {}
// 获取名称
function getName() {}
命名方法:大写驼峰式命名法,首字母大写。
命名规范:前缀为名称。
class Persion {
  constructor(name) {
   ...
  }
}
let person = new Person('张三');
类的成员包括:
class Person {
  // 私有属性 
  _name: string;
  constructor() { }
  // 公共方法
  getName() {
    return this._name;
  }
  // 公共方法
  setName(name) {
    this._name = name;
  }
}
格式化插件推荐prettier
// 设置标题
setTitle()
/*
* 代码执行到这里后会调用setTitle()函数
* setTitle():设置title的值
*/
setTitle();
6.3 函数 & 方法注释
/**
 * 函数说明
 * @关键字
 **/
常用关键字注释
注释名     语法     含义     示例
@param     @param {参数类型} 描述信息     描述参数     @param {String} name 传入名称
@return     @return {参数类型} 描述信息     描述返回值     @retun {Boolean} true: 可执行; false: 不可执行
@author     @author 描述信息     描述作者     @author 某某某 2018/04/24
@example     @example 示例代码     演示函数的使用     @example setTitle(‘啦啦啦’);
方法一:indexOf
var arr=[2,8,5,0,5,2,6,7,2];
function array_unique(arr){
  var result = [];
  for (var i = 0; i < arr.length; i++) {
     if(hash.indexOf(arr[i])==-1){
      result.push(arr[i]);
     }
  }
  return result;
}
方法二:filter
var  arr = [1, 2, 3, 1, 2, 3, 4, 5, 5];
var  resultArr;
resultArr = arr.filter( function  (item, index, self) {
   return  self.indexOf(item) == index;
});
mutations:改变、变异
store:仓库
state:状态
Action:事件
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
        //可以添加其他代码
        state.count++
    }
  }
})
// 改变状态
// 通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化
store.commit('increment')
//获取状态值
console.log(store.state.count) // -> 1
const app = new Vue({
  el: '#app',
  // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})
// 子组件使用
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}
1、slice截取起止下标位置字符串
str.slice(start,end);
2、substring截取起止下标位置字符串
str.substring(start,end);
3、substr不建议使用
str.substr(start,length);
1、string.split():根据字符分割字符串
// split:分裂
var str = 'abc#edf#ghi';
var arr = str.split('#')
2、String.substring(start,end):根据给定的开始位置和结束位置获取子字符串
//substring:子字符串
var str = 'abcedfghi';
var arr = str.substring(2,4); // ce
3、String.substr(start,length):根据给定的开始位置和截取长度获取子字符串
//substr:子字符串
var str = 'abcedfghi';
var arr = str.substring(2,4); // bced
4、String.slice(start,end):根据给定的开始位置和结束位置获取子字符串
//substring:子字符串
var str = 'abcedfghi';
var arr = str.substring(2,4); // ce
总结:
slice,substring,substr 都是用来截取字符串,但是只有slice支持数组;三者如果不传参数,则都返回全部内容;
参数为正数时,只有substring会自动调换顺序,slice在第一参数大于第二参数时会无效返回空,而substr无所谓,除非给定的第一参数超出了源数据长度才会返回空;
参数为负数时,只有substring会永远无效,即不要给substring使用负值!slice可认为从尾部倒数,或者直接用源数据长度加上这个负值换算为正数,然后结论依然遵循第2条所述;而substr,则只适用第一参数为负数,换算方法同slice,其第二参数代表截取的个数,是不能为负数的;
据w3cshool所提示 (链接: http://www.w3school.com.cn/jsref/jsref_substr.asp) ,“ECMAscript 没有substr方法进行标准化,因此反对使用它”。
自己的理解:
slice 唯独它顺序有严格要求,两个参数都支持从尾部倒数,或者数据长度加上这个负值
substring 唯独它可以自动换顺序;
substr 唯独它第二个参数是个数的意思; 第一个参数为负数的时候支持从尾部倒数,或者数据长度加上这个负值
1、“+”:加号拼接
var str = 'abc' + 'efg';//'abcefg'
2、数组拼接成字符串
var arr = ['abc','def','ghi']
var str = arr.join('');// 'abcdefghi'
3、反引号:模版字符串
var str_1 = '你好'
var str_2 = `${str_1} 世界`;// 'abcdefghi'
4、concat():合并数组,合并字符串
var str_1 = '你好'
var str_2 = '世界';
var str_3 = str_1.concat(str_2)
replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
语法:str.replace(regexp|substr, newSubStr|function)
参数1:必需,正则表达式或者字符串
参数2:必需,替换文本或生成替换文本的函数
案例一:只替换一个
var str = '你好,世界!你好,这个世界!'
var new_str = str.replace('世界','地球') //'你好,地球!你好,这个世界!'
var new_str = str.replace(/世界/,'地球') //'你好,地球!你好,这个世界!'
案例二:全局替换
var str = '你好,世界!你好,这个世界!'
var new_str_2 = str.replace(/世界/g,'地球') //'你好,地球!你好,这个地球!'
案例三:字符串首字母替换成大写
var str = 'hello world!'
var new_str = str.replace(/\b\w+\b/g,function(word){
  return word.substring(0,1).toUpperCase()+word.substring(1);
}) //'Hello World'
// 创建并挂载到 #app (会替换 #app)
new MyComponent().$mount('#app');
// 手动挂载
new myAppendTo().$mount().$appendTo('#app');//appendTo