js 参数传递 
先贴一段《JavaScript 高级程序设计》的介绍
ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样。
原始类型(按值传递) 
js
function addTen(num) {
  num += 10;
  console.log(num); // 30
}
let count = 20;
addTen(count);
console.log(count); // 20引用类型(按共享传递) 
js
// case1
function changeName(obj) {
  obj.name = "newName";
  console.log(obj.name); // 'newName'
}
let person = {
  name: "cx",
};
changeName(person);
console.log(person.name); // 'newName'
// case2
function changeName(obj) {
  obj = { name: "newName" };
  console.log(obj.name); // 'newName'
}
let person = {
  name: "cx",
};
changeName(person);
console.log(person.name); // 'cx'那为什么 case1 能按引用传递呢?因为基本类型值存储于栈内存中,传递的就是当前值,修改不会影响原有变量的值。而引用类型在栈中存的是地址,传递的是地址索引,修改的是该地址在堆内存中的值。例子 2 中将obj重新赋值为另一个地址,所以它不会影响到person对象。
call 
分析 call 
具体用法 
js
function showVal() {
  console.log("实参", ...arguments);
  return `当前值是${this.value}`;
}
var value = 2;
const obj = {
  value: 1,
};
showVal.call(obj, { aa: 111 }, { b: 222 }); // // 实参 { aa: 111 } { b: 222 } 当前值是1
showVal.call(null, { aa: 111 }, { b: 222 }); //  // 实参 { aa: 111 } { b: 222 } 当前值是2简要分析 
- 改变了this指向
- 运行了showVal函数
- 能传递指定参数
- 不传this参数时,this指向全局(浏览器指向window,node 环境指向global/globalThis)
实现 call 
步骤 
- 将调用的函数设置为对象的属性
- 执行该函数并返回值
- 删除该函数
实现 
js
Function.prototype.myCall1 = function (context) {
  let args = Array.prototype.slice.call(arguments, 1);
  context ??= globalThis;
  context.fn = this;
  let result = context.fn(...args);
  delete context.fn;
  return result;
};
//测试
window.value = 2;
function showVal() {
  console.log("实参", ...arguments);
  return `当前值是${this.value}`;
}
const obj = {
  value: 1,
};
console.log(showVal.myCall1(obj, { aa: 111 }, { b: 222 })); // 实参 { aa: 111 } { b: 222 } 当前值是1
console.log(showVal.myCall1(null, { aa: 111 }, { b: 222 })); // 实参 { aa: 111 } { b: 222 } 当前值是2优化 
js
Function.prototype.myCall2 = function (context, ...args) {
  context ??= globalThis;
  let fnSymbol = Symbol(); // 保证键的唯一性
  context[fnSymbol] = this;
  let result = context[fnSymbol](...args);
  delete context[fnSymbol];
  return result;
};apply 
分析与实现 
与 call 类似,不过入参为数组,若不是数组则直接调用函数且参数为非对象类型时报错
js
Function.prototype.myApply = function (context, args) {
  if (typeof args !== "object")
    throw new TypeError("CreateListFromArrayLike called on non-object");
  context ??= globalThis;
  args = Array.isArray(args) ? args : [];
  let fnSymbol = Symbol();
  context[fnSymbol] = this;
  let result = context[fnSymbol](...args);
  delete context[fnSymbol];
  return result;
};
window.value = 2;
function showVal() {
  console.log("实参", ...arguments);
  return `当前值是${this.value}`;
}
const obj = {
  value: 1,
};
console.log(showVal.apply(obj, { aa: 111 }, { b: 222 })); // 实参  当前值是1
console.log(showVal.myApply(obj, [{ aa: 111 }, { b: 222 }])); // 实参 { aa: 111 } { b: 222 } 当前值是1
console.log(showVal.myApply(null, { aa: 111 }, { b: 222 })); // 实参  当前值是2bind 
分析 bind 
具体用法 
js
var value = 2;
function showVal() {
  console.log("实参", ...arguments);
  return `当前值是${this.value}`;
}
const obj = {
  value: 1,
};
const _bind = showVal.bind(obj, "params1");
console.log(_bind("params2")); // 实参 params1 params2;  当前值是1简要分析 
- 改变this指向,返回一个函数
- bind函数和它返回的函数都可以接收参数
实现 bind 
js
Function.prototype.myBind = function (context) {
  const self = this;
  let args = Array.prototype.slice.call(arguments, 1);
  return function () {
    let bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(context, args.concat(bindArgs));
  };
};
// rest参数写法
Function.prototype.myBind2 = function (context, ...args) {
  return (...args2) => {
    return this.call(context, ...args, ...args2);
  };
};
window.value = 2;
function showVal() {
  console.log("实参", ...arguments);
  return `当前值是${this.value}`;
}
const obj = {
  value: 1,
};
const _bind = showVal.myBind(null, "params1");
console.log(_bind("params2")); // 实参 params1 params2;  当前值是1构造函数效果模拟 
一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
也就是当 bind 返回的函数当构造函数使用时,调用 bind 时传入的 this 会失效,但其他参数依然会生效
bind 演示 
js
window.value = 2;
function showVal(name, age) {
  this.name = name;
  console.log("值:", this.value, "参数", arguments);
}
const obj = {
  value: 1,
};
const newBind = showVal.bind(obj, "cx");
newBind("params2"); //  值: 1 参数: Arguments(2) ['cx', 'params2']
console.log("----------------");
const foo = new newBind(24); // 值: undefined 参数: Arguments(2) ['cx', 24]
console.log(foo); // showVal { name: 'cx' }构造函数效果实现 
js
function showVal(name, age) {
  this.name = name;
  console.log("值:", this.value, "参数", arguments);
}
showVal.prototype.xxx = "xxx";
const obj = {
  value: 1,
};
Function.prototype.myBind = function (context, ...args) {
  const self = this;
  //空函数进行中转
  const Fntemp = function () {};
  const FBound = function (...args2) {
    return self.apply(this instanceof Fntemp ? this : context, [
      ...args,
      ...args2,
    ]);
  };
  // FBound.prototype = this.prototype // 直接修改 fBound.prototype 的时候,也会直接修改绑定函数的 prototype
  Fntemp.prototype = this.prototype;
  FBound.prototype = new Fntemp();
  return FBound;
};
const newBind = showVal.myBind(obj, "cx");
newBind("params2"); //  值: 1 参数: Arguments(2) ['cx', 'params2']
console.log("----------------");
const foo = new newBind(24); // 值: undefined 参数: Arguments(2) ['cx', 24]
console.log(foo); // FBound {name: 'cx'}new 
new 的过程 
《JavaScript 高级程序设计》里是这么介绍的
- 在内存中创建一个新对象
- 这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性
- 构造函数内部的this被赋值为这个新对象(即this指向新对象)
- 执行构造函数内部的代码(给新对象添加属性)
- 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象
实现 new 
js
function newFn(fatherFn, ...args) {
  if (typeof fatherFn !== "function")
    return `the frist param must be a function`;
  let obj = Object.create(fatherFn.prototype);
  let res = fatherFn.call(obj, ...args);
  return res instanceof Object ? res : obj;
}
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.xxx = "xxx";
  return null;
}
Person.prototype.say = function () {
  console.log(`I am ${this.name}, I'm ${this.age} years old. `);
};
// 测试一下
const person1 = newFn(Person, "cx", 24);
console.log(person1); // Person { name: 'cx', age: 24, xxx: 'xxx' }
person1.say(); // I am cx, I'm 24 years old.