关于箭头函数的补充

箭头函数补充

关于watch侦听器和箭头函数的理解-CSDN博客

一、prototype属性

大家可以先看一下这个简短的视频,大体了解,等读下面文章相对轻松

JavaScript的prototype是什么?原型链是什么?_哔哩哔哩_bilibili

prototype的定义

它主要存在于函数对象中(包括构造函数和普通的函数),用于实现基于原型的继承。prototype属性指向一个对象,这个对象包含了所有实例共享的属性和方法。当创建一个新的函数对象时,JavaScript会自动为这个新函数对象添加一个prototype属性,这个属性的值是一个拥有constructor属性的空对象constructor属性指向该函数对象本身,形成一个循环引用)。

理解prototype

  1. 原型链:JavaScript中的对象通过原型链(prototype chain)实现继承。当尝试访问一个对象的属性或方法时,如果对象本身没有这个属性或方法,JavaScript会查找对象的原型(即对象的[[prototype]]属性,注意__proto__是一个非标准但广泛支持的属性,实际开发中应使用Object.getPrototypeOf()方法来访问原型),如果在原型上找到了就返回该值;如果原型上也没有,就继续查找原型的原型,直到找到Object.prototype(所有对象的最终原型),如果Object.prototype上也没有找到,则返回undefined

  2. 构造函数和prototype:当使用构造函数(具有new关键字的函数)创建一个新对象时,新对象的内部__proto__会被链接到构造函数的prototype对象上。这意味着,通过构造函数创建的实例可以访问到prototype对象上的属性和方法。

  3. 修改prototype:可以在任何时候修改构造函数的prototype对象,添加或删除属性或方法。这种修改会立即影响到所有通过该构造函数创建的实例,因为它们都共享同一个原型对象。但是,如果直接在实例上添加属性或方法,这些修改不会影响到其他实例或原型对象。

function Person(name, age) {  
  this.name = name;  
  this.age = age;  
}  
  
// 在Person的prototype上添加方法  
Person.prototype.greet = function() {  
  console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old.");  
};  
  
var person1 = new Person("Alice", 30);  
var person2 = new Person("Bob", 25);  
  
person1.greet(); // 输出: Hello, my name is Alice and I am 30 years old.  
person2.greet(); // 输出: Hello, my name is Bob and I am 25 years old.  
  
// 修改prototype  
Person.prototype.farewell = function() {  
  console.log("Goodbye!");  
};  
  
person1.farewell(); // 输出: Goodbye!  
person2.farewell(); // 输出: Goodbye!

在这个例子中,Person是一个构造函数,它的prototype对象被用来添加greetfarewell方法。所有通过new Person(...)创建的实例都可以访问到这些方法。

 简单了解prototype、_proto_和constructor

__proto__

 1.定义
__proto__(写法是两边各有两个下划线_)属性是对象独有的,它其实是[[prototype]]的引用, 每个实例(通过构造函数生成的实例都是对象函数也是对象,所以函数也有__proto__)上面都有__proto__ 属性,它指向原型对象(构造函数的prototype属性),以下面的代码为例进行说明:

function Person(){} 
let person1 = new Person()
person1.__proto__ === Person.prototype  // true

先强调一下通过new构造函数生成的实例都是对象(可以参考new构造函数之后执行的步骤_全参构造在new的途中会执行-CSDN博客),所以person1是一个实例对象。也可以这么理解,每个实例上面都有__proto__ 属性。所以person1具有__proto__属性,__proto__属性指向构造函数的prototype(原型对象)person1.proto === Person.prototype,它俩的指向是一样的。

这里补充说明一下:[[prototype]]属性是隐藏的,属于内部特性,开发者不能直接在JavaScript中直接访问这些特性。不过目前大部分新浏览器实现方式是使用__proto__来表示。

2.作用
它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(可以理解为父对象)里找,如果父对象也不存在这个属性,则继续往父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找…直到原型链顶端null(可以理解为祖宗。。。),再往上找就相当于在null上取值,会报错(可以理解为,再往上就已经不是“人”的范畴了,找不到了,到此结束,null为原型链的终点),由以上这种通过__proto__属性来连接对象直到null的一条链即为我们所谓的原型链

function Person(){} 
let person1 = new Person()
person1.__proto__ === Person.prototype  // true

原型链实现了继承。
person1中一个属性在本身里面找不到,会到__proto__属性所指向的那个对象(也就是原型对象,可以称为父对象)中找,也就是Person.prototype,Person.prototype是一个对象,所以也有__proto__属性,如果在Person.prototype也找不到,会去Person.prototype.__proto__中寻找,也就是person1.__proto__.__proto__(Person.prototype.__proto__ === person1.__proto__.__proto__),如果还找不到,就继续往上找,Person.prototype.__proto__.__proto__,直到最后Person.prototype.__proto__.__proto__.__proto__ === null,也就是person1.__proto__.__proto__.__proto__.__proto__ === null
其实我们平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠__proto__继承而来的。

3.总结
__proto__属性是对象上的属性,一般都是通过构造函数生成的实例对象所具有的属性,指向构造函数的prototype的属性(原型对象),也可以说指向了共享对象,即实例的原型对象(共享对象)。所以可以说__proto__属性就是原型对象(prototype),原型对象本身也有__proto__,指向Object.prototype,最后Object.prototype.__proto__指向了null。null是原型链的顶端。

补充:绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.proto 时,可以理解成返回了 Object.getPrototypeOf(obj)。

constructor

1.定义

单从constructor这个属性来讲,只有prototype对象才有,每个函数在创建的时候,JS会同时创建一个该函数对应的prototype对象,
该函数创建的对象.__proto__ === 该函数.prototype,
该函数.prototype.constructor === 该函数本身

故通过函数创建的对象即使自己没有constructor属性,它也能通过__proto__找到对应的constructor,所以任何对象最终都可以找到其构造函数(null如果当成对象的话,将null除外)。
示例如下:

function Person(){} 
let person1 = new Person()

person1对象本身不具有constructor属性,所以会通过__proto__属性到原型链中找,而person1.proto=== Person.prototype,Person.prototype具有constructor属性并指向了Person,所以person1.constructor指向了Person,它不是person1自己本身拥有的,是继承而来的。

从广义上来说:constructor属性是对象才拥有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数,每个对象都有构造函数(本身拥有或继承而来,继承而来的要结合__proto__属性查看会更清楚点),Function这个对象比较特殊,它的构造函数就是它自己(因为Function可以看成是一个函数,也可以是一个对象),所有函数和对象最终都是由Function构造函数得来,所以constructor属性的终点就是Function这个函数。

2. 作用
constructor属性不影响任何JavaScript的内部属性。instanceof检测对象的原型链,通常你是无法修改的(不过某些引擎通过私有的__proto__属性暴露出来)。

constructor其实没有什么用处,只是JavaScript语言设计的历史遗留物。由于constructor属性是可以变更的,所以未必真的指向对象的构造函数,只是一个提示。不过,从编程习惯上,我们应该尽量让对象的constructor指向其构造函数,以维持这个惯例。

不过在一些场景下,还是很有用的,示例如下:

var person1;
(function(){
  function Person (name) {
    this.name = name;
  }

  Person.prototype.sayName= function () {
    console.log(this.name);
  }
  person1 = new Person('qingyun');
})()
person1.sayName();//qingyun

console.log(Person); //Father is not defined

因为Person在闭包中,当我们想对Person类增加方法时可以通过

person1.constructor.prototype.sayAge = function(age){
    console.log(age);
}
person1.sayAge('20'); //20

有人说,也可以直接这样写

person1.__proto__.sayAge = function(age){
    console.log(age);
}
person1.sayAge('20'); //20

__proto__这个属性是浏览器厂商自己内置的,有的浏览器是不支持的,所以使用constructor更可靠一点。

总结

单从constructor这个属性来讲,只有prototype对象才有。每个函数在创建的时候,JS会同时创建一个该函数对应的prototype对象,而函数创建的对象.proto === 该函数.prototype该函数.prototype.constructor===该函数本身。故通过函数创建的对象即使自己没有constructor属性,它也能通过__proto__找到对应的constructor,所以任何对象最终都可以找到其构造函数(null如果当成对象的话,将null除外)。记住,单从constructor这个属性来讲,只有prototype对象才有,所以才能实现继承

闭包解释

在JavaScript中,闭包通常是通过在一个函数内部创建另一个函数,并返回这个内部函数来实现的。

它允许一个函数访问并操作函数之外的变量。闭包是由函数以及创建该函数的词法环境组合而成的。这个环境包含了这个闭包创建时所能访问的所有局部变量。由于闭包的存在,使得函数外部的参数和变量在闭包内部被引用,因此这些参数和变量不会被垃圾回收机制回收,从而延长了它们的生命周期。闭包可以封装一些私有变量,这些变量在外部无法直接访问,只能通过闭包提供的特定接口来访问和修改

 更详细图解请查看帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)_js prototype constructor-CSDN博客

 二、arguments对象

arguments 是一个对应于传递给函数的参数的类数组对象。

arguments对象是所有(非箭头)函数中都可用的局部变量.

可以使用arguments对象在函数中引用函数的参数.索引从0开始.

arguments对象是一个伪数组. 除了length和索引外,不能用任何数组的方法

 基本用法

  1. 访问所有参数:

在函数体内,你可以通过arguments对象访问所有传递给函数的参数,即使函数没有显式地定义这些参数。 

function showArguments() {  
    for (let i = 0; i < arguments.length; i++) {  
        console.log(arguments[i]);  
    }  
}  
  
showArguments(1, 2, 'a', 'b');  
// 输出:  
// 1  
// 2  
// a  
// b
2. 获取参数数量

 arguments.length属性提供了传递给函数的参数数量。

function func() {  
    console.log(arguments.length);  
}  
  
func(1, 2, 3); // 输出: 3
3. 索引访问

 可以通过索引访问 arguments 中的元素,也可以通过 length 属性获取参数数量,就像在数组中一样。例如,arguments[0] 是第一个参数,arguments[1] 是第二个参数,依此类推。

function foo() {
  console.log(arguments); // [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5
  console.log(arguments[3]); // 4
  console.log(arguments.length); // 5
}
foo(1, 2, 3, 4, 5);
4. 函数作用域内有效

arguments 对象只在函数体内部有效。它是每个函数作用域内的特殊对象。 

5. callee 属性

 arguments.callee 是一个指向当前执行的函数的引用。这在匿名函数中特别有用,因为它允许函数引用自身。

// 阶乘函数
let factorial = function(n) {
    if (n <= 1) return 1;
    return n * arguments.callee(n - 1);
};
 
console.log(factorial(5)); // 120

这里 arguments.callee 指向当前执行的函数(即匿名函数),用于递归计算阶乘。

内容选自:

​​​​​​Js进阶30-类数组对象-arguments_js中数组的agument-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值