JavaScript_기본 지식의 프로토타입 학습 가이드
프로토타입이란
함수 유형에는 프로토타입으로 직접 번역되는 속성 프로토타입이 있습니다. 이 속성은 개체를 가리키는 포인터입니다. 이 개체에는 일부 속성과 메서드가 포함되어 있으며 이러한 속성과 메서드는 현재 함수에 의해 생성된 모든 인스턴스(객체)에서 공유됩니다.
앞서 말한 내용을 토대로 잘 생각해보면 다음과 같은 코드를 얻을 수 있습니다.
function Person(){ ... } Person.prototype = { country : 'china', sayName : function(){ ... } }
먼저 함수 유형인 person의 인스턴스를 생성한 다음 person의 메소드 프로토타입이 객체이므로 객체를 가리키도록 선언됩니다. 이 개체의 속성과 메서드는 현재 개인 함수에 의해 생성된 인스턴스에 의해 공유됩니다. 즉,
person1 = new Person(); person2 = new Person();
Person1과 person2는 둘 다 Person 함수 유형 인스턴스를 통해 재생성된 인스턴스입니다. 둘 다 Person.prototype이 가리키는 개체를 직접 가리키는 포인터(__proto__)를 갖고 있기 때문에 동일한 속성인 country와 메서드를 갖습니다. 그러나 __proto__ 포인터는 Chrome 및 Firefox와 같은 브라우저에서만 정의됩니다. 실제로 이 속성은 프로토타입을 이해하는 데 사용되지 않습니다.
프로토타입 등의 활용에 대해서는 추후 더 자세히 다루도록 하겠습니다.
객체 생성 모드
다음으로 객체를 생성하는 방법과 일반적인 패턴, 그리고 장점과 단점을 살펴보겠습니다.
1. 공장 모드
공장과 마찬가지로 특정 객체를 생성하는 과정을 추상화하고 함수를 사용하여 특정 인터페이스로 객체 생성의 세부 사항을 캡슐화합니다. 중복된 작업 중 일부를 대체하는 함수를 사용하여 코드는 다음과 같습니다.
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson("jiangshui","22","engineer");
이렇게 사람이 생성됩니다. 팩토리 패턴은 유사한 객체가 여러 개 반복적으로 생성되는 문제를 해결하지만 객체 인식 문제는 해결하지 않습니다. 인간을 주형으로 만든 것인지, 동물을 주형으로 만든 것인지에 관계없이 단순히 대상을 생성한 것이며, 대상의 종류를 구별할 수 없다.
2. 생성자 패턴
사용자 정의 생성자를 만들어 사용자 정의 개체 유형의 속성과 메서드를 정의합니다.
function Person(name, age, job){ this.name = name; this.age = age; this.job = jpb; this.sayName = function(){ alert(this.name); }; }; var person1 = new Person(...);
3. 생성자 패턴과 팩토리 패턴의 차이점:
- 명시적인 개체 생성이 없습니다.
- 이 개체에 속성과 메서드를 직접 할당합니다.
- 반품문의가 없습니다.
Person은 함수 유형의 개체입니다. new 이후에는 개체가 계속 생성되지만 새로 생성된 이 개체는 매개변수가 함수에 전달되고 this 포인터에 할당되므로 전달된 내용은 The가 됩니다. 새로 생성된 객체의 속성이나 메서드.
생성자의 기본 규칙은 첫 글자를 대문자로 표기하는 것입니다. 위 코드의 실행은 다음 단계를 거칩니다.
- 새 개체 만들기
- 새 객체에 생성자 범위 할당
- 생성자에서 코드 실행
- 새 개체 반환
이런 방식으로 생성된 인스턴스에는 기본적으로 생성자를 가리키는 생성자 속성이 포함됩니다. 예:
alert(person1.constructor == Person);
그래서 생성자 패턴을 사용하면 유형 간의 구분이 있으며 해당 인스턴스를 특정 유형으로 식별할 수 있습니다.
그리고 생성자는 일반 함수이기 때문에 새로운 객체를 얻기 위해서는 피드백이 필요하기 때문에 new로 호출됩니다. 그렇지 않은 경우 직접 실행하는 것은 일반 함수와 동일합니다. 예를 들어 위의 Person.sayName()을 실행하면 window.name이 팝업됩니다. 함수는 창 아래에서 실행되므로 이것이 창을 가리킵니다.
생성자 패턴에도 결함이 있습니다. 생성자 패턴의 메서드는 각 인스턴스에서 다시 생성되므로 다른 인스턴스에서 동일한 이름을 가진 함수는 동일하지 않습니다. 예:
person1.sayName == person2.sayName; //false
즉, 생성자에 의해 생성된 각 객체 인스턴스, 속성 및 메서드는 고유하며 복사됩니다. 객체가 다르기 때문에 속성을 고유하게 만드는 것이 필요하지만 많은 메소드 함수와 코드가 동일하므로 여러 번 반복하면 리소스가 낭비됩니다.
따라서 함수를 외부에 배치한 다음 포인터를 사용하여 생성자에서 이 함수를 가리킬 수 있습니다. 그런 다음 생성된 인스턴스에서 메서드는 특정 함수에 대한 포인터를 저장하고 동일한 함수를 공유합니다.
function Person(name, age){ this.name = name; this.age = age; this.sayName = sayName; } function sayName(){ alert(this.name); }
但是这样,这个函数就变成了全局函数,而且与 Person 构造函数关联性不强,没有封装性可言。
下面有请原型模式登场。
原型模式
前面已经介绍了一部分关于原型的基础知识。简单的说,就是每个函数都有一个 prototype 属性,指向一个对象(原型对象),这个对象里面可以放一些属性或者方法。然后这个函数生成的实例,会有一个不规范的属性(__proto__)指向原型。
由此来看,你应该可以理解:prototype 产生的属性和方法是所有实例共享的。
这样正好解决了上面构造函数模式中,实例中函数的共用问题。例如下面代码:
function Person(){ .... } Person.prototype.name = "jiangshui"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); //jiangshui
或者
Person.prototype = { constructor : Person, name : "jiangshui", sayName : function(){ alert(this.name); } };
第二种方法覆盖了整个 prototype 对象,所以需要手动指定 constructor 属性,指向构造函数否则会指向 Object。
梳理一下它们的关系:
- Person.prototype -》 原型对象,可以定义一些属性或者参数,被所有实例共用。
- Person.prototype.constructor == Person —》 原型对象有个默认的属性 constructor 指向该原型对象所属的构造函数(注意另一种写法会覆盖掉这个属性,需要重新指定)。
- person1 = new Person() -》构造函数生成实例,实例包含了构造函数的内容和原型对象的内容。
- person1.__proto__ -》指向创建这个实例的原型对象(不规范,不要用)。
使用 isPrototypeOf() 可以确定对象之间的关系。例如:
Person.prototype.isPrototypeOf(person1);
当代码读取某个对象的某个属性,会执行搜索。先从当前对象开始,如果没有,则搜索指针指向的原型对象,而不会搜索构造函数。对象实例可以访问但是不能重写原型对象的值。如果实例中设置了与原型对象同名的属性,则搜索过程,在实例中结束而不会访问原型对象,所以达到覆盖的目的。因此即使这个属性设置为 null,也表示在实例中已经存在该属性,而不会取消掉这个属性,从而可以访问原型对应属性。
所以需要使用 delete 操作符,完全删除实例属性,从而可以重新访问原型。
原型是动态的,对原型对象所做的任何修改,都能立即从实例上反映出来。原因是实例与原型之间的松散链接关系,每次调用实例的属性方法,都会进行一次查询,如果原型变了,查询结果也就变了。
了解原型之后,我们也可以对原生对象添加新方法或属性。Object、Array、String 等原生引用类型,与上面构造函数类似,我们可以用 prototype 扩充它们的方法。例如:
String.prototype.startsWith = function(text){ return this.indexOf(text) == 0; }; var msg = "Hello World"; msg.startsWith("Hello");
这段代码为 String 这个原生引用类型,增加了一个 startsWith 方法,功能就是传递进去一个参数,看看要测试的字符串是否以参数开始。由于原型的动态性,所以只要执行一下,所有字符串类型的变量全都获得了这个方法。
但是不推荐使用这个方法,如果用的太多,代码太多,会导致维护困难、代码混乱等情况。一般情况下,会先继承某个原生引用类型,然后再在新自定义的类型上创建。关于继承,后面会再总结。
原型模式也不是万能的,原型中的所有属性和方法是被所有实例共享的,所以对于函数之类非常合适,而对于包含引用类型的属性来说,就会产生一些冲突。例如:
function Person(){} Person.prototype = { constructor : Person, friends : ["greg","jack"] }; var person1 = new Person(); var person2 = new Person(); person1.friends.push("tom"); console.log(person2.friends);
你会在 console 中看到,person2 的 friends 多了一个 tom,这并不是我想要的,但是对 person1 定义他的朋友时,的确影响到了实例 person2。
所以我们要结合原型模式和构造函数模式来使用。
组合使用构造函数模式和原型模式
这就是最常用的模式,构造函数用来定义实例属性,通过传递参数实现自定义;原型用来定义方法或者需要所有实例共享的属性。这样,既实现了自定义,又保证了共用,还避免了问题。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["greg","jack"]; } Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } }; var jiangshui = new Person("jiangshui","22","engineer");
实际应用示例
OK,到了这里,你可能会看懂原型是啥,以及如何创建对象,可是,这些又有什么用?确实,我之前的工作,一直也就是用 jQuery 写一些代码就可以了,根本用不到封装然后生成对象实现功能等。那这些究竟有什么用?
这种开发方式主要用于模块化和组建化的开发。比如你常用的弹窗功能,你当然可以把弹窗有关代码,每次都粘贴复制,然后修改一下就可以用在项目里面了。更好的选择是把你的弹窗功能代码,抽象封装成这样的一个组件,这样当你需要用弹窗的时候,只需要传递参数生成一个弹窗实例,就可以调用了。
原型对象和原型链
在Javascript中,万物皆对象,但对象也有区别,大致可以分为两类,即:普通对象(Object)和函数对象(Function)。
一般而言,通过new Function产生的对象是函数对象,其他对象都是普通对象。
举例说明:
function f1(){ //todo } var f2 = function(){ //todo }; var f3 = new Function('x','console.log(x)'); var o1 = {}; var o2 = new Object(); var o3 = new f1(); console.log( typeof f1,//function typeof f2,//function typeof f3,//function typeof o1,//object typeof o2,//object typeof o3 //object ); >> function function function object object object
f1属于函数的声明,最常见的函数定义方式,f2实际上是一个匿名函数,把这个匿名函数赋值给了f2,属于函数表达式,f3不常见,但也是一种函数对象。
Function是JS自带的对象,f1,f2在创建的时候,JS会自动通过new Function()的方式来构建这些对象,因此,这三个对象都是通过new Function()创建的。
在Javascript中创建对象有两种方式:对象字面量和使用new表达式,o1和o2的创建恰好对应了这两种方式,重点讲一下o3, 如果用Java和C#的思路来理解的话,o3是f1的实例对象,o3和f1是同一类型,至少我以前这么认为,其实不然…
那么怎么理解呢? 很简单,看o3是不是通过new Function产生的, 显然不是,既然不是函数对象,那就是普通对象 。
通过对函数对象和普通对象的简单理解之后,我们再来了解一下Javascript中的原型和原型链:
在JS中,每当创建一个函数对象f1 时,该对象中都会内置一些属性,其中包括prototype和__proto__, prototype即原型对象,它记录着f1的一些属性和方法。
需要注意的是,prototype 对f1是不可见的,也就是说,f1不会查找prototype中的属性和方法。
function f(){} f.prototype.foo = "abc"; console.log(f.foo); //undefined
那么,prototype有什么用呢? 其实prototype的主要作用就是继承。 通俗一点讲,prototype中定义的属性和方法都是留给自己的“后代”用的,因此,子类完全可以访问prototype中的属性和方法。
想要知道f1是如何把prototype留给“后代”,我们需要了解一下JS中的原型链,此时,JS中的 __proto__ 入场了,这哥们长的很奇特,隐藏的也很深,以致于你经常见不到它,但它在普通对象和函数对象中都存在, 它的作用就是保存父类的prototype对象,JS在通过new 表达式创建一个对象的时候,通常会把父类的prototype赋值给新对象的__proto__属性,这样,就形成了一代代传承…
function f(){} f.prototype.foo = "abc"; var obj = new f(); console.log(obj.foo); //abc
现在我们知道,obj中__proto__保存的是f的prototype, 那么f的prototype中的__proto__中保存的是什么呢? 看下图:
如图所示,f.prototype的__proto__中保存的是Object.prototype,Object.prototype对象中也有__proto__,而从输出结果看,Object.prototype.__proto__ 是null,表示obj对象原型链的终结。如下图所示:
obj对象拥有这样一个原型链以后,当obj.foo执行时,obj会先查找自身是否有该属性,但不会查找自己的prototype,当找不到foo时,obj就沿着原型链依次去查找…
在上面的例子中,我们在f的prototype上定义了foo属性,这时obj就会在原型链上找到这个属性并执行。

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











얼굴 검출 및 인식 기술은 이미 상대적으로 성숙하고 널리 사용되는 기술입니다. 현재 가장 널리 사용되는 인터넷 응용 언어는 JS입니다. 웹 프런트엔드에서 얼굴 감지 및 인식을 구현하는 것은 백엔드 얼굴 인식에 비해 장점과 단점이 있습니다. 장점에는 네트워크 상호 작용 및 실시간 인식이 줄어 사용자 대기 시간이 크게 단축되고 사용자 경험이 향상된다는 단점이 있습니다. 모델 크기에 따라 제한되고 정확도도 제한됩니다. js를 사용하여 웹에서 얼굴 인식을 구현하는 방법은 무엇입니까? 웹에서 얼굴 인식을 구현하려면 JavaScript, HTML, CSS, WebRTC 등 관련 프로그래밍 언어 및 기술에 익숙해야 합니다. 동시에 관련 컴퓨터 비전 및 인공지능 기술도 마스터해야 합니다. 웹 측면의 디자인으로 인해 주목할 가치가 있습니다.

함수 상속에서는 "기본 클래스 포인터" 및 "파생 클래스 포인터"를 사용하여 상속 메커니즘을 이해합니다. 기본 클래스 포인터가 파생 클래스 개체를 가리키는 경우 상향 변환이 수행되고 기본 클래스 멤버에만 액세스됩니다. 파생 클래스 포인터가 기본 클래스 개체를 가리키는 경우 하향 캐스팅이 수행되므로(안전하지 않음) 주의해서 사용해야 합니다.

js와 vue의 관계: 1. 웹 개발의 초석인 JS 2. 프론트엔드 프레임워크로서의 Vue.js의 등장 3. JS와 Vue의 상호 보완적인 관계 4. JS와 Vue의 실제 적용 Vue.

상속 오류 디버깅 팁: 올바른 상속 관계를 확인하세요. 디버거를 사용하여 코드를 단계별로 실행하고 변수 값을 검사합니다. 가상 수정자를 올바르게 사용했는지 확인하세요. 숨겨진 상속으로 인해 발생하는 상속 다이아몬드 문제를 살펴봅니다. 추상 클래스에서 구현되지 않은 순수 가상 함수를 확인하세요.

상속과 다형성은 클래스 결합에 영향을 줍니다. 상속은 파생 클래스가 기본 클래스에 종속되기 때문에 결합을 증가시킵니다. 다형성은 객체가 가상 함수와 기본 클래스 포인터를 통해 일관된 방식으로 메시지에 응답할 수 있기 때문에 결합을 줄입니다. 모범 사례에는 상속을 적게 사용하고, 공용 인터페이스를 정의하고, 기본 클래스에 데이터 멤버를 추가하지 않고, 종속성 주입을 통해 클래스를 분리하는 것이 포함됩니다. 다형성과 종속성 주입을 사용하여 은행 계좌 애플리케이션에서 결합을 줄이는 방법을 보여주는 실제 예입니다.

C++ 함수 상속에 대한 자세한 설명: "is-a"와 "has-a" 사이의 관계를 마스터하세요. 함수 상속이란 무엇인가요? 함수 상속은 파생 클래스에 정의된 메서드를 기본 클래스에 정의된 메서드와 연결하는 C++의 기술입니다. 파생 클래스가 기본 클래스의 메서드에 액세스하고 재정의할 수 있도록 하여 기본 클래스의 기능을 확장합니다. "is-a" 및 "has-a" 관계 함수 상속에서 "is-a" 관계는 파생 클래스가 기본 클래스의 하위 유형, 즉 파생 클래스가 기본 클래스의 특성과 동작을 "상속"함을 의미합니다. 기본 클래스. "has-a" 관계는 파생 클래스에 기본 클래스 개체에 대한 참조 또는 포인터가 포함되어 있음을 의미합니다. 즉, 파생 클래스가 기본 클래스 개체를 "소유"합니다. 구문다음은 함수 상속을 구현하는 방법에 대한 구문입니다. classDerivedClass:pu

Genshin Impact 버전 4.4의 새로운 지도를 소개합니다. Genshin Impact 4.4 버전은 또한 Liyue에서 Sea Lantern Festival을 열었습니다. 동시에 버전 4.4에서는 Shen Yu Valley라는 새로운 지도 영역이 출시됩니다. 제공된 정보에 따르면 Shen Yugu는 실제로 Qiaoying Village의 일부이지만 플레이어는 Shen Yugu라고 부르는 데 더 익숙합니다. 이제 새로운 맵을 소개하겠습니다. Genshin Impact 버전 4.4의 새로운 지도 소개 버전 4.4에서는 Liyue 북쪽에 "Chenyu Valley·Shanggu", "Chenyu Valley·Nanling" 및 "Laixin Mountain"이 개방됩니다. 계곡·상구' . ※마신 퀘스트·액트 3: 용과 자유의 노래 프롤로그를 완료하면 순간이동 앵커 포인트가 자동으로 잠금 해제됩니다. 2. Qiaoyingzhuang 따뜻한 봄바람이 천위의 산과 들에 다시 한 번 스며들 때, 향기로운

객체 지향 프로그래밍이란 무엇입니까? 객체 지향 프로그래밍(OOP)은 실제 엔터티를 클래스로 추상화하고 객체를 사용하여 이러한 엔터티를 나타내는 프로그래밍 패러다임입니다. 클래스는 객체의 속성과 동작을 정의하고, 객체는 클래스를 인스턴스화합니다. OOP의 가장 큰 장점은 코드를 더 쉽게 이해하고 유지 관리하며 재사용할 수 있다는 것입니다. OOP의 기본 개념 OOP의 주요 개념에는 클래스, 개체, 속성 및 메서드가 포함됩니다. 클래스는 객체의 속성과 동작을 정의하는 객체의 청사진입니다. 객체는 클래스의 인스턴스이며 클래스의 모든 속성과 동작을 갖습니다. 속성은 데이터를 저장할 수 있는 개체의 특성입니다. 메소드는 객체의 데이터에 대해 작동할 수 있는 객체의 함수입니다. OOP의 장점 OOP의 주요 장점은 다음과 같습니다. 재사용성: OOP는 코드를 더 많이 만들 수 있습니다.
