JavaScript进阶3之参数按值传递、call,apply,bind和new的实现、继承的多种方式

本文详细探讨了JavaScript中的参数传递机制,包括按值传递和共享传递,并解释了如何实现call、apply、bind和new。此外,文章还分析了JavaScript中继承的多种方式,如原型链继承、构造函数借用、组合继承、原型继承和寄生式继承,以及它们的优缺点。
摘要由CSDN通过智能技术生成

参数按值传递

按值传递

  1. ECMAScript中所有函数的参数都是按值传递的。
  2. 按值传递:把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。
var value = 1;
function foo(v) {
   
    v = 2;
    console.log(v); //2
}
foo(value);
console.log(value) // 1

内存分布:
改变前:
在这里插入图片描述
改变后:
在这里插入图片描述

当传递 value 到函数 foo 中,相当于拷贝了一份 value,假设拷贝的这份叫 _value,函数中修改的都是 _value 的值,而不会影响原来的 value 值。

共享传递

按引用传递:传递对象的引用(真实的物理地址),函数内部对参数的任何改变都会影响该对象的值,因为两者引用的是同一个对象。

var obj = {
   
    value: 1
};
function foo(o) {
   
    o.value = 2;
    console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2

改变前:
在这里插入图片描述
在这里插入图片描述

改变后:
在这里插入图片描述
在这里插入图片描述

var obj = {
   
    value: 1
};
function foo(o) {
   
    o = 2;
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

共享传递是指,在传递对象的时候,传递的是地址索引。
所以修改 o.value,可以通过引用找到原值,但是直接修改 o,并不会修改原值。所以上面两个例子其实都是按共享传递。
改变前:
在这里插入图片描述
在这里插入图片描述

改变后:
在这里插入图片描述
在这里插入图片描述

函数传递参数 ,传递的是参数的拷贝:

  1. 指针拷贝,拷贝的是地址索引;
  2. 常规类型拷贝,拷贝的是值 ;

call、apply、bind和new的实现

call,apply,bind方法解决的是:this指向

this

this首先需要跟执行上下文挂钩
绑定方式:软绑定,硬绑定(call、apply、bind)

软绑定

this.name="window"
function test() {
   
    console.log(this.name);
}
const zhangsan = {
   
    name: "zhangsan",
    test: test,
};
test()
zhangsan.test()

在这里插入图片描述

硬绑定

function test() {
   
    console.log(this.name);
}
const xiaoming={
   name:'xiaoming'}
//硬绑定
test.call(xiaoming)
test.apply(xiaoming)
const t=test.bind(xiaoming)
t()

在这里插入图片描述
注意两点:

  1. call 改变了 this 的指向,指向到 xiaoming;
  2. test 函数执行了;

call的实现

call() :在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

第一步

使用软绑定的方式实现call,

所以我们模拟的步骤可以分为:

  1. 将函数设为对象的属性;
  2. 执行该函数;
  3. 删除该函数;
// 第一步
// test 是对象的属性名,反正最后也要删除它,所以起什么都可以。
xiaoming.test = test
// 第二步
xiaoming.test()
// 第三步
delete xiaoming.test

根据上述思路,提供一版:

// 第一版
Function.prototype.call2 = function(context) {
   
    // 首先要获取调用call的函数,用this可以获取,this指向的是 bar
    context.fn = this;
    context.fn();
    delete context.fn;  //卸磨杀驴
}

// 测试一下
 let foo = {
   
    value: 1
};

function bar() {
   
    console.log(this.value);
}

bar.call2(foo); // 1

第二步

call除了可以指定this,还可以指定参数

var foo = {
   
    value: 1
};

function bar(name, age) {
   
    console.log(name)
    console.log(age)
    console.log(this.value);
}

bar.call(foo, 'kevin', 18);
// kevin
// 18
// 1

在这里插入图片描述

可以从 Arguments 对象中取值,取出第二个到最后一个参数,然后放到一个数组里。
上述代码的Arguments中取第二个到最后一个的参数

// 以上个例子为例,此时的arguments为:
// arguments = {
   
//      0: foo,
//      1: 'kevin',
//      2: 18,
//      length: 3
// }
// 因为arguments是类数组对象,所以可以用for循环
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
   
    args.push('arguments[' + i + ']');
}

// 执行后 args为 ["arguments[1]", "arguments[2]", "arguments[3]"]

接下来使用eval拼接成一个函数

eval('context.fn(' + args +')')

考虑到目前大部分浏览器在console中限制eval的执行,也可以使用rest
此处代码为:

// 第二版
Function.prototype.call2 = function(context) {
   
    context.fn = this;
    let arg = [...arguments].slice(1)  //arguments是类数组对象,将其拆分,并转为数组,然后再从第一个参数开始截取
    context.fn(...arg)  //arg数组拆分,逐步传入
    delete context.fn;
}

// 测试一下
var foo = {
   
    value: 1
};

function bar(name, age) {
   
    console.log(name)
    console.log(age)
    console.log(this.value);
}

bar.call2(foo, 'kevin', 18); 
// kevin
// 18
// 1

在这里插入图片描述

第三步

  1. this 参数可以传 null,当为 null 的时候,视为指向 window
    举个例子:
var value = 1;

function bar() {
   
    console.log(this.value
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值