目录

JS基础

<script>function (){...}</script>
<script src="demo.js" defer></script> 

基础概念


var obj = {
  // __proto__
  __proto__: theProtoObj,
  // Shorthand for ‘handler: handler’
  handler,
  // Methods
  toString() {
    // Super calls
    return "d " + super.toString();
  },
  // Computed (dynamic) property names
  ["prop_" + (() => 42)()]: 42,
};
// Map
sayings.set("dog", "woof");
sayings.set("cat", "meow");
sayings.set("elephant", "toot");
sayings.size; // 3
sayings.get("dog"); // woof
sayings.get("fox"); // undefined
sayings.has("bird"); // false
sayings.delete("dog");
sayings.has("dog"); // false
// 循环Map
for (const [key, value] of sayings) {
  console.log(`${key} goes ${value}`);
}

// Set
const mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");

mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2
// 循环Set
for (const item of set) {
  console.log(item);
}
// 函数声明
foo();
function foo(){}
// 函数表达式
baz();
var baz = function(){}

对象


// 对象字面量
const person = {
  name: ["Bob", "Smith"],
  age: 32,
  bio: function () { // 可以简写为 bio(){ }
    console.log(`${this.name[0]} ${this.name[1]} 现在 ${this.age} 岁了。`);
  },
  introduceSelf: function () {
    console.log(`你好!我是 ${this.name[0]}。`);
  },
};
function createPerson(name) {
  const obj = {};
  obj.name = name;
  obj.introduceSelf = function () {
    console.log(`你好!我是 ${this.name}。`);
  };
  return obj;
}
const salva = createPerson('Salva');
function Person(name) {
  this.name = name;
  this.introduceSelf = function () {
    console.log(`你好!我是 ${this.name}。`);
  };
}
const salva = new Person("Salva");
class Person {
  name;
  constructor(name) {
    this.name = name;
  }
  introduceSelf() {
    console.log(`Hi! I'm ${this.name}`);
  }
}
const giles = new Person("Giles");
var o = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(x) {
    this.a = x / 2;
  },
};

console.log(o.a); // 7
console.log(o.b); // 8
o.c = 50;
console.log(o.a); // 25
ℹ️note

使用 gettersetter 时相当于定义了新的属性
定义一个已经声明的函数作为的 getter 和 setter 方法,使用Object.definePropertyObject.prototype.defineGetter()Object.prototype.defineSetter()

const o = {};

o.a = 1;
// 等价于:
Object.defineProperty(o, "a", {
  value: 1,
  writable: true,
  configurable: true,
  enumerable: true,
});

function Archiver() {
  let temperature = null;
  const archive = [];

  Object.defineProperty(this, "temperature", {
    get() {
      console.log("get!");
      return temperature;
    },
    set(value) {
      temperature = value;
      archive.push({ val: temperature });
    },
  });

  this.getArchive = () => archive;
}

原型


console.log(person.__proto__ === Person.prototype) // true
console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__ === null) // true
❗️important
  • __proto__ 属性是原型链的基础,所有对象(包括函数)都通过 __proto__ 形成一条通往 Object.prototype 的链条,从而实现了继承
  • prototype 是函数特有的属性,而普通对象只有 __proto__ 属性
  • prototype 属性用于定义所有通过该构造函数创建的实例共享的属性和方法。
  • constructorprototype 对象自有的属性,指向构造函数 Person
  • 对象的原型链:实例的 person__proto__ 属性指向它的构造函数 Personprototype 属性,Personprototype 属性的 __proto__ 属性又指向 Objectprototype 属性,Objectprototype 属性指向 null,即 person -> Person.prototype -> Object.prototype -> null
  • 函数的原型链:所有函数(PersonObject 甚至是 Function) 也是对象,其 __proto__ 属性指向 Function 的 prototype 属性,即 Person.__proto__ = Function.prototypeObject.__proto__ = Function.prototypeFunction.__proto__ = Function.prototype,而 Function.prototype 的 __proto__ 属性又指向 Objectprototype 属性
  • 函数在 JS 中有双重身份。一是作为构造函数,通过 .prototype 为它的实例提供原型链(橙色链)。二是作为普通对象(是 Function 函数的实例),它自己也有 __proto__,指向 Function.prototype,形成函数对象的原型链

// 一个构造函数
function Box(value) {
  this.value = value;
}

// 使用 Box() 构造函数创建的所有盒子都将具有的属性
Box.prototype.getValue = function () {
  return this.value;
};

const boxes = [new Box(1), new Box(2), new Box(3)];
// 对象字面量(没有 `__proto__` 键)自动将 `Object.prototype` 作为它们的 `[[Prototype]]`
const object = { a: 1 };
Object.getPrototypeOf(object) === Object.prototype; // true

// 数组字面量自动将 `Array.prototype` 作为它们的 `[[Prototype]]`
const array = [1, 2, 3];
Object.getPrototypeOf(array) === Array.prototype; // true

// 正则表达式字面量自动将 `RegExp.prototype` 作为它们的 `[[Prototype]]`
const regexp = /abc/;
Object.getPrototypeOf(regexp) === RegExp.prototype; // true

赋值、浅拷贝、深拷贝

const original = {
  name: 'Alice',
  age: 25,
  hobbies: ['reading', 'coding']
};
  1. 直接赋值:通常指的是将一个对象的引用赋值给另一个变量,而不是复制对象本身。这意味着两个变量指向同一个对象。
const copy = original; // 直接赋值
copy.hobbies.push('painting');
copy.name='zmy';

console.log(original); // {name:'Alice, age:25, hobbies:Array(3)}
console.log(copy);     // {name:'Alice, age:25, hobbies:Array(3)}
console.log(original.hobbies); // 输出 ["reading", "coding", "painting"]
console.log(copy.hobbies);     // 输出 ["reading", "coding", "painting"]
  1. 浅拷贝:浅拷贝创建了一个新的对象,并复制了原始对象的顶层属性。如果属性是引用类型(如对象或数组),则复制的是这些引用类型的引用,而不是实际的内容。
const shallowCopy = Object.assign({}, original); // 浅拷贝
shallowCopy.hobbies.push('painting');
shallowCopy.name='zmy';

console.log(original);    // {name:'Alice, age:25, hobbies:Array(3)}
console.log(shallowCopy); // {name:'zmy, age:25, hobbies:Array(3)}
console.log(original.hobbies); // 输出 ["reading", "coding", "painting"]
console.log(shallowCopy.hobbies); // 输出 ["reading", "coding", "painting"]
  1. 深拷贝:深拷贝不仅复制顶层属性,还会递归地复制对象内的所有引用类型。这意味着新对象及其所有嵌套的对象都是完全独立的,与原对象没有关联。
// 使用 JSON.parse 和 JSON.stringify 进行深拷贝
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.hobbies.push('painting');
deepCopy.name='zmy';

console.log(original);    // {name:'Alice, age:25, hobbies:Array(2)}
console.log(deepCopy); // {name:'zmy, age:25, hobbies:Array(3)}
console.log(original.hobbies); // 输出 ["reading", "coding"]
console.log(deepCopy.hobbies); // 输出 ["reading", "coding", "painting"]

this问题

Promise

❗️important

Promise就是一个承诺,预期会完成某事(异步操作),然后把对完成后的结果的处理方式(成功和失败完成的回调函数)告诉 Promise,预期的事完成后,他会自动去处理。

// 直接传回调函数
createAudioFileAsync(audioSettings, successCallback, failureCallback);
// 绑定回调函数
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
doSomething()
  .then((url) => {
    // fetch(url) 前缺少 `return` 关键字。
    fetch(url);
  })
  .then((result) => {
    // result 是 undefined,因为上一个处理器没有返回任何东西。
    // 无法得知 fetch() 的返回值,也无法知道它是否成功。
  });
💡tip

经验法则是,每当你的操作遇到一个 Promise,就返回它,并把它的处理推迟到下一个 then 处理器中。

const listOfIngredients = [];

doSomething()
  .then((url) => {
    // fetch 调用前面现在包含了 `return` 关键字。
    return fetch(url)
      .then((res) => res.json())
      .then((data) => {
        listOfIngredients.push(data);
      });
  })
  .then(() => {
    console.log(listOfIngredients);
    // listOfIngredients 现在将包含来自 fetch 调用的数据。
  });

// 将嵌套链扁平化为单链,这样更简单,也更容易处理错误
doSomething()
  .then((url) => fetch(url))
  .then((res) => res.json())
  .then((data) => {
    listOfIngredients.push(data);
  })
  .then(() => {
    console.log(listOfIngredients);
  });

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    if(Math.random() < 0.5)
      resolve(value);
    else
      reject(reason);
  }, 300);
});
myPromise.then(onFulfilled, onRejected);
❗️important
  • 在 Promise 中,一旦调用了 resolve,则该 Promise 的状态立即变为已兑现,会立马调用 then 中的兑现回调函数 onFulfilled,传给 resolve 的参数(即Promise的兑现值 value)也是传给 onFulfilled 的参数(也可以理解为用 onFulfilled 替换 resolve),onFulfilled 的返回值又称为 then 返回的 Promise 对象的兑现值。如果 onFulfilled 不是一个函数,则内部会被替换为一个恒等函数((x) => x),它只是简单地将兑现值向后传递。
  • 同样的,调用了 reject,Promise 就变为已拒绝,立即调用 onRejected,传给 rejct 的参数(拒绝值,通常是一个 Error 对象)也就是传给 onRejected 的参数,它的返回值成为 catch 返回的 Promise 的兑现值。如果 onRejected 不是一个函数,则内部会被替换为一个抛出器函数((x) => { throw x; }),它会抛出它收到的拒绝原因。
  • 如果 valuereason 中有任意一个被省略,Promise 将会被兑现(fulfilled)或拒绝(rejected)为 undefined
const funcPromise = (x) =>
  new Promise((resolve, reject) => {
    console.log('resolve1',resolve)
    console.log('reject1',reject)
    func(x, (error, result) => {
      console.log('error',error)
      console.log('result',result)
      if (error) {
        console.log('reject2',reject)
        reject(new Error(result));
      } else {
        console.log('resolve2',resolve)
        resolve(result*2);
      }
    });
  });
const funcPromise = (x) =>
  new Promise((resolve, reject) => {
    console.log('resolve1',resolve)
    console.log('reject1',reject)
    func(x, (error, result) => {
      console.log('error',error)
      console.log('result',result)
      if (error) {
        console.log('reject2',reject)
        reject(new Error(result));
        console.log('rejected')
        throw 'reject error'
      } else {
        console.log('resolve2',resolve)
        resolve(result*2);
        console.log('resolved')
        throw 'resolve error'
      }
    });
  });
function func(x,f){
  if(x<5){
    // throw new Error('a error')
    // throw 'a error'
    f(true, 3)
  }
  else{
    console.log('success')
    f(false,x)
  }
}
funcPromise(4)
  .then((result) => console.log('the result is ',result))
  .catch((error) => console.error("读取文件失败"+error.message));
const fetchPromise = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);

console.log(fetchPromise);

fetchPromise.then((response) => {
  console.log(`已收到响应:${response.status}`);
});

console.log("已发送请求……");
const fetchPromise = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);

fetchPromise
  .then((response) => response.json())
  .then((data) => {
    console.log(data[0].name);
  });
const fetchPromise = fetch(
  "bad-scheme://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);

fetchPromise
  .then((response) => {
    if (!response.ok) {
      throw new Error(`HTTP 请求错误:${response.status}`);
    }
    return response.json();
  })
  .then((json) => {
    console.log(json[0].name);
  })
  .catch((error) => {
    console.error(`无法获取产品列表:${error}`);
  });
const fetchPromise1 = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);
const fetchPromise2 = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/not-found",
);
const fetchPromise3 = fetch(
  "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json",
);

Promise.all([fetchPromise1, fetchPromise2, fetchPromise3])
  .then((responses) => {
    for (const response of responses) {
      console.log(`${response.url}:${response.status}`);
    }
  })
  .catch((error) => {
    console.error(`获取失败:${error}`);
  });

[func1, func2, func3]
  .reduce((p, f) => p.then(f), Promise.resolve())
  .then((result3) => {
    /* 使用 result3 */
  });

// 上面的代码等同于:
Promise.resolve()
  .then(func1)
  .then(func2)
  .then(func3)
  .then((result3) => {
    /* 使用 result3 */
  });
💡tip
  • reduce() 方法对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
  • 第一次执行回调函数时,不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行,则需要传递初始值。否则,数组索引为 0 的元素将被用作初始值,迭代器将从第二个元素开始执行(即从索引为 1 而不是 0 的位置开始)。
const array1 = [1, 2, 3, 4];

// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  initialValue,
);

console.log(sumWithInitial);
// Expected output: 10

async/await 和 Promise 对比

// 定义:不用 async/await
get<T>(url: string): Promise<T> {
  return axiosInstance.get<BaseResponse<T>>(url)
    .then(res => res.data.result)
}

// 调用方式 A:用 .then()
http.get<User>('/user/1')
  .then(user => {
    console.log(user.name)  // user: User
  })

// 调用方式 B:用 await(在 async 函数内)
async function fetchUser() {
  const user = await http.get<User>('/user/1')
  console.log(user.name)
}
// 定义:用 async/await
async get<T>(url: string): Promise<T> {
  const res = await axiosInstance.get<BaseResponse<T>>(url)  // ✅ 内部 await
  return res.data.result
}

// 调用方式 A:用 await(在 async 函数内)
async function fetchUser() {
  const user = await http.get<User>('/user/1')  // ✅ 可以!
  console.log(user.name)
}

// 调用方式 B:用 .then()
http.get<User>('/user/1')
  .then(user => {
    console.log(user.name)  // ✅ 也可以!
  })

客户端Web API

JS模块

ℹ️note

默认导出不加大括号,因为每个模块只能有一个默认导出,默认导入时为匿名函数命名

// inside module.js
export { function1 as newFunctionName, function2 as anotherNewFunctionName };

// inside main.js
import { newFunctionName, anotherNewFunctionName } from "/modules/module.js";
// 获取 module.js 中所有可用的导出
import * as Module from "/modules/module.js";
Module.function1();
Module.function2();

jQuery

jQuery.noConflict();
// 局部使用,将 $ 传递给函数
jQuery(document).ready(function( $ ) {
    // You can use the locally-scoped $ in here as an alias to jQuery.
    $( "div" ).hide();
});
// 使用立即调用函数表达式
(function( $ ) {
    // Your jQuery code here, using the $
})( jQuery );
// 作为 setter
$( "a" ).attr( "href", "allMyHrefsAreTheSameNow.html" );
// 设定多个属性值
$( "a" ).attr({
    title: "all titles are the same too!",
    href: "somethingNew.html"
});
// 作为getter
$( "a" ).attr( "href" )
$( "div.foo" ).has( "p" );         // div.foo elements that contain <p> tags
$( "h1" ).not( ".bar" );           // h1 elements that don't have a class of bar
$( "ul li" ).filter( ".current" ); // unordered list items with class of current
$( "ul li" ).first();              // just the first unordered list item
$( "ul li" ).eq( 5 );              // the sixth, it return a jQuery object that can be call other method chainly
$( "ul li" ).get( 5 );              // the sixth, it return native DOM element itself, not a jQuery object.
$( "ul li" )[ 5 ];                  // equivalent to $( "ul li" ).get( 5 );
ℹ️note
  • Attributes 是 HTML 标签的一部分,通常用于初始化 DOM 对象的属性。
  • Properties 是 DOM 对象的一部分,反映了元素的当前状态,可以动态变化。

ajax

// Using the core $.ajax() method
$.ajax({
 
    // The URL for the request
    url: "post.php",
 
    // The data to send (will be converted to a query string)
    data: {
        id: 123
    },
 
    // Whether this is a POST or GET request
    type: "GET",
 
    // The type of data we expect back
    dataType : "json",
})
  // Code to run if the request succeeds (is done);
  // The response is passed to the function
  .done(function( json ) {
     $( "<h1>" ).text( json.title ).appendTo( "body" );
     $( "<div class=\"content\">").html( json.html ).appendTo( "body" );
  })
  // Code to run if the request fails; the raw request and
  // status codes are passed to the function
  .fail(function( xhr, status, errorThrown ) {
    alert( "Sorry, there was a problem!" );
    console.log( "Error: " + errorThrown );
    console.log( "Status: " + status );
    console.dir( xhr );
  })
  // Code to run regardless of success or failure;
  .always(function( xhr, status ) {
    alert( "The request is complete!" );
  });
$.ajax({
  url: '请求的URL',
  type: '请求类型 (GET, POST, PUT, DELETE等)',
  data: '发送的数据',
  dataType: '预期服务器返回的数据类型',
  async: true | false // 默认 true
  success: function(response) {
    // 请求成功后的回调函数
  },
  error: function(xhr, status, error) {
    // 请求失败后的回调函数
  }
});
// 发送文件+普通参数
var formData = new FormData();
formData.append("file", $("#file")[0].files[0]);
formData.append("userId", 1001);
formData.append("type", "image");
formData.append("ids[]", "abc");//传递数组
formData.append("ids[]", "def");

$.ajax({
  url: "/upload",
  type: "POST",
  data: formData,
  processData: false, // 不处理数据
  contentType: false, // 不设置 contentType
  success: function (res) {
    console.log("上传成功", res);
  },
  error: function (err) {
    console.error("上传失败", err);
  }
});

// 后端接参
@PostMapping("/test")
public String test(
    @RequestParam("file") MultiPartFile file,
    @RequestParam("ids[]") List<String> ids,
    @RequestParam("type") String type,
    @RequestParam("userId") String userId
) {
    System.out.println(ids);
    return "ok";
}
fetch('https://api.example.com/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ key1: 'value1', key2: 'value2' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
axios.get('https://api.example.com/data')
    .then(function (response) {
        console.log(response.data);
    })
    .catch(function (error) {
        console.error(error);
    });