JavaScript의 new 키워드는 인스턴스화와 상속을 구현할 수 있지만 개인적으로는 new 키워드를 사용하는 것이 최선의 방법은 아니며, 좀 더 친숙한 구현이 있을 수 있다고 생각합니다. 이 글에서는 new 키워드 사용 시의 문제점을 소개하고, 더 빠르고 이해하기 쉬운 구현을 제공하기 위해 new와 관련된 일련의 객체 지향 작업을 캡슐화하는 방법을 소개합니다.
전통적인 인스턴스화 및 상속
두 개의 클래스, <a href="http://www.php.cn/wiki/164.html" target="_blank가 있다고 가정합니다. ">Class<code><a href="http://www.php.cn/wiki/164.html" target="_blank">Class</a>:function Class() {}
:function Class() {} 및 SubClass:function SubClass(){}
, SubClass는 Class에서 상속해야 합니다. 전통적인 메소드는 일반적으로 다음 단계에 따라 구성 및 구현됩니다. Class의
-
상속된속성과 메소드는 Class In에 배치되어야 합니다. SubClass
의 프로토타입 속성
은 자신의 메소드와 속성 도 자신의 프로토타입 속성
- SubClass의 프로토타입 객체의 프로토타입(proto) 속성은 반드시 클래스의 프로토타입을 가리켜야 합니다
new SubClass() Object.create(Class.prototype) | | V V SubClass.prototype ---> { } { }.proto ---> Class.prototype구체적인 예를 들어 보겠습니다. 다음 코드에서는 다음 작업을 수행합니다.
- 정의 Human
- 이라는 상위 클래스는 Human
- 을 상속하는 Man이라는 하위 클래스를 정의합니다. 상위 클래스의
// 构造函数/基类 function Human(name) { this.name = name; } /* 基类的方法保存在构造函数的prototype属性中 便于子类的继承 */ Human.prototype.say = function () { console.log("say"); } /* 道格拉斯的object方法(等同于object.create方法) */ function object(o) { var F = function () {}; F.prototype = o; return new F(); } // 子类构造函数 function Man(name, age) { // 调用父类的构造函数 Human.call(this, name); // 自己的属性age this.age = age; } // 继承父类的方法 Man.prototype = object(Human.prototype); Man.prototype.constructor = Man; // 实例化子类 var man = new Man("Lee", 22); console.log(man); // 调用父类的say方法: man.say();DEMO위 코드를 통해 몇 가지 전통적인 인스턴스화 및 상속 기능을 요약할 수 있습니다. 🎜>
- 기존 메소드의 "클래스"는 생성자여야 합니다.
- 속성과 메소드는 프로토타입 속성에 바인딩되며 상속은 프로토타입 특성의 도움으로 구현됩니다.
- 객체를 인스턴스화하려면 new 키워드를 사용하세요.
- Object.create 메소드가 Douglas의 객체 메소드와 일치한다고 확신하는 이유는 무엇입니까? MDN에서 객체 메소드는 Object.create에 대한 Polyfill 솔루션입니다:
"Javascript: The Good Parts"에서 Douglas는 이를 피해야 한다고 믿습니다. 새 키워드 사용:
생성자 함수를 호출할 때 새 접두사를포함하는 것을 잊어버린 경우 이는 새 개체에 바인딩되지 않습니다. 안타깝게도 전역 개체에 바인딩됩니다. 따라서 새 개체를 확장하는 대신 전역 변수를 방해하게 됩니다. 컴파일 경고도 없고 런타임 경고도 없습니다. (49페이지)
일반적인 아이디어는 다음과 같습니다. new를 사용해야 할 때 new 키워드를 잊어버리면 몇 가지 문제가 발생할 수 있습니다.
물론, 사용하는 것을 잊어버린 키워드는 일련의 문제를 일으키게 됩니다. 한발 뒤로 물러서면 이 문제는 완전히 피할 수 있습니다.
function foo() { // 如果忘了使用关键字,这一步骤会悄悄帮你修复这个问题 if ( !(this instanceof foo) ) return new foo(); // 构造函数的逻辑继续…… }
또는 보다 일반적인
예외를 던지거나 그냥 function foo()
{
if ( !(this instanceof arguments.callee) )
throw new Error("Constructor called as a function");
}
하거나 John Resig의 솔루션을 따르거나 makeClass 팩토리 함수를 준비하세요. 그리고 대부분의 초기화 함수를 생성자 자체 대신 init 메소드에 넣습니다.
// makeClass - By John Resig (MIT Licensed) function makeClass(){ return function(args){ if ( this instanceof arguments.callee ) { if ( typeof this.init == "function" ) this.init.apply( this, args.callee ? args : arguments ); } else return new arguments.callee( arguments ); }; }
제 생각에 new 키워드가 좋은 습관이 아닌 주요 이유는 다음과 같습니다.
...new는 JavaScript가 "인기"를 얻기 위해 Java와 같은 구문을 수용했던 시절의 잔재입니다. Visual Basic과 같은 보완 언어가 Microsoft의 언어 계열에서 C++에 있었던 것처럼 우리는 이를 Java의 동생으로 추진했습니다. Douglas는 이 문제를 다음과 같이 설명했습니다. 이 간접적인 방법은 고전적인 교육을 받은 프로그래머에게 언어를 더 친숙하게 보이도록 하기 위한 것이었지만 Java 프로그래머가 JavaScript의 생성자 패턴에 대해 가지고 있는 매우 낮은 의견에서 알 수 있듯이 그렇게 하지 못했습니다. 또한 JavaScript의 진정한 프로토타입 특성을 모호하게 만들었습니다. 결과적으로 언어를 효과적으로 사용하는 방법을 아는 프로그래머는 거의 없습니다.간단히 말하면 JavaScript는 프로토타입 언어입니다. 처음 만들어졌을 때 시장의 요구에 부응하고 사람들이 Java와 동일하다는 느낌을 갖도록 하기 위해 새로운 키워드가 도입되었습니다. Javascript는 프로토타입 기능을 통해 인스턴스화 및 상속을 구현하도록 되어 있지만 새 키워드로 인해 설명이 불가능해졌습니다.
把传统方法加以改造
既然new关键字不够友好,那么我们有两个办法可以解决这个问题:一是完全抛弃new关键字,二是把含有new关键字的操作封装起来,只向外提供友好的接口。下面将介绍第二种方法的实现思路,把传统方法加以改造。
我们开始构造一个最原始的基类Class
(类似于JavaScript中的Object类),并且只向外提供两个接口:
Class.extend 用于拓展子类
Class.create 用于创建实例
// 基类 function Class() {} // 将extend和create置于prototype对象中,以便子类继承 Class.prototype.extend = function () {}; Class.prototype.create = function () {}; // 为了能在基类上直接以.extend的方式进行调用 Class.extend = function (props) { return this.prototype.extend.call(this, props); }
extend和create的具体实现:
Class.prototype.create = function (props) { /* create实际上是对new的封装; create返回的实例实际上就是new构造出的实例; this即指向调用当前create的构造函数; */ var instance = new this(); /* 绑定该实例的属性 */ for (var name in props) { instance[name] = props[name]; } return instance; } Class.prototype.extend = function (props) { /* 派生出来的新的子类 */ var SubClass = function () {}; /* 继承父类的属性和方法, 当然前提是父类的属性都放在prototype中 而非上面create方法的“实例属性”中 */ SubClass.prototype = Object.create(this.prototype); // 并且添加自己的方法和属性 for (var name in props) { SubClass.prototype[name] = props[name]; } SubClass.prototype.constructor = SubClass; /* 介于需要以.extend的方式和.create的方式调用: */ SubClass.extend = SubClass.prototype.extend; SubClass.create = SubClass.prototype.create; return SubClass; }
仍然以Human和Man类举例使用说明:
var Human = Class.extend({ say: function () { console.log("Hello"); } }); var human = Human.create(); console.log(human) human.say(); var Man = Human.extend({ walk: function () { console.log("walk"); } }); var man = Man.create({ name: "Lee", age: 22 }); console.log(man); // 调用父类方法 man.say(); man.walk();
DEMO
至此,基本框架已经搭建起来,接下来继续补充功能。
我们希望把构造函数独立出来,并且统一命名为init。就好像
Backbone.js
中每一个view都有一个initialize
方法一样。这样能让初始化更灵活和标准化,甚至可以把init构造函数借出去我还想新增一个子类方法调用父类同名方法的机制,比如说在父类和子类的中都定义了一个say方法,那么只要在子类的say中调用
this.callSuper()
就能调用父类的say方法了。例如:
// 基类 var Human = Class.extend({ /* 你需要在定义类时定义构造方法init */ init: function () { this.nature = "Human"; }, say: function () { console.log("I am a human"); } }) var Man = Human.extend({ init: function () { this.sex = "man"; }, say: function () { // 调用同名的父类方法 this.callSuper(); console.log("I am a man"); } });
那么Class.create就不仅仅是new一个构造函数了:
Class.create = Class.prototype.create = function () { /* 注意在这里我们只是实例化一个构造函数 而非最后返回的“实例”, 可以理解这个实例目前只是一个“壳” 需要init函数对这个“壳”填充属性和方法 */ var instance = new this(); /* 如果对init有定义的话 */ if (instance.init) { instance.init.apply(instance, arguments); } return instance; }
实现在子类方法调用父类同名方法的机制,我们可以借用John Resig的方案:
Class.extend = Class.prototype.extend = function (props) { var SubClass = function () {}; var _super = this.prototype; SubClass.prototype = Object.create(this.prototype); for (var name in props) { // 如果父类同名属性也是一个函数 if (typeof props[name] == "function" && typeof _super[name] == "function") { // 重新定义用户的同名函数,把用户的函数包装起来 SubClass.prototype[name] = (function (super_fn, fn) { return function () { // 如果用户有自定义callSuper的话,暂存起来 var tmp = this.callSuper; // callSuper即指向同名父类函数 this.callSuper = super_fn; /* callSuper即存在子类同名函数的上下文中 以this.callSuper()形式调用 */ var ret = fn.apply(this, arguments); this.callSuper = tmp; /* 如果用户没有自定义的callsuper方法,则delete */ if (!this.callSuper) { delete this.callSuper; } return ret; } })(_super[name], props[name]) } else { // 如果是非同名属性或者方法 SubClass.prototype[name] = props[name]; } .. } SubClass.prototype.constructor = SubClass; }
最后给出一个完整版,并且做了一些优化:
function Class() {} Class.extend = function extend(props) { var prototype = new this(); var _super = this.prototype; for (var name in props) { if (typeof props[name] == "function" && typeof _super[name] == "function") { prototype[name] = (function (super_fn, fn) { return function () { var tmp = this.callSuper; this.callSuper = super_fn; var ret = fn.apply(this, arguments); this.callSuper = tmp; if (!this.callSuper) { delete this.callSuper; } return ret; } })(_super[name], props[name]) } else { prototype[name] = props[name]; } } function Class() {} Class.prototype = prototype; Class.prototype.constructor = Class; Class.extend = extend; Class.create = Class.prototype.create = function () { var instance = new this(); if (instance.init) { instance.init.apply(instance, arguments); } return instance; } return Class; }
下面是测试的代码。为了验证上面代码的健壮性,故意实现了三层继承:
var Human = Class.extend({ init: function () { this.nature = "Human"; }, say: function () { console.log("I am a human"); } }) var human = Human.create(); console.log(human); human.say(); var Man = Human.extend({ init: function () { this.callSuper(); this.sex = "man"; }, say: function () { this.callSuper(); console.log("I am a man"); } }); var man = Man.create(); console.log(man); man.say(); var Person = Man.extend({ init: function () { this.callSuper(); this.name = "lee"; }, say: function () { this.callSuper(); console.log("I am Lee"); } }) var person = Person.create(); console.log(person); person.say();
DEMO
是时候彻底抛弃new关键字了
如果不使用new关键字,那么我们需要转投上两节中反复使用的Object.create
来生产新的对象
假设我们有一个矩形对象:
var Rectangle = { area: function () { console.log(this.width * this.height); } };
借助Object.create,我们可以生成一个拥有它所有方法的对象:
var rectangle = Object.create(Rectangle);
生成之后,我们还可以给这个实例赋值长宽,并且取得面积值
var rect = Object.create(Rectangle); rect.width = 5; rect.height = 9; rect.area();
注意这个过程我们没有使用new关键字,但是我们相当于实例化了一个对象(rectangle),给这个对象加上了自己的属性,并且成功调用了类(Rectangle)的方法。
但是我们希望能自动化赋值长宽,没问题,那就定义一个create方法:
var Rectangle = { create: function (width, height) { var self = Object.create(this); self.width = width; self.height = height; return self; }, area: function () { console.log(this.width * this.height); } };
使用方式如下:
var rect = Rectangle.create(5, 9); rect.area();
在纯粹使用Object.create的机制下,我们已经完全抛弃了构造函数这个概念。一切都是对象,一个类也可以是对象,这个类的实例不过是一个它自己的复制品。
下面看看如何实现继承。我们现在需要一个正方形,继承自这个长方形
var Square = Object.create(Rectangle); Square.create = function (side) { return Rectangle.create.call(this, side, side); }
实例化它:
var sq = Square.create(5); sq.area();
这种做法其实和我们第一种最基本的类似
function Man(name, age) { Human.call(this, name); this.age = age; }
上面的方法还是太复杂了,我们希望进一步自动化,于是我们可以写这么一个extend函数
function extend(extension) { var hasOwnProperty = Object.hasOwnProperty; var object = Object.create(this); for (var property in extension) { if (hasOwnProperty.call(extension, property) || typeof object[property] === "undefined") { object[property] = extension[property]; } } return object; } /* 其实上面这个方法可以直接绑定在原生的Object对象上:Object.prototype.extend 但个人不推荐这种做法 */ var Rectangle = { extend: extend, create: function (width, height) { var self = Object.create(this); self.width = width; self.height = height; return self; }, area: function () { console.log(this.width * this.height); } };
这样当我们需要继承时,就可以像前几个方法一样用了
var Square = Rectangle.extend({ // 重写实例化方法 create: function (side) { return Rectangle.create.call(this, side, side); } }) var s = Square.create(5); s.area();
结束语
本文对去new关键字的方法做了一些罗列,但工作还远远没有结束,有非常多的地方值得拓展,比如:如何重新定义instance of
方法,用于判断一个对象是否是一个类的实例?如何在去new关键字的基础上继续实现多继承?希望本文的内容在这里只是抛砖引玉,能够开拓大家的思路。
위 내용은 JavaScript에서 new 키워드를 사용하지 않는 이유에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

실제 세계에서 JavaScript의 응용 프로그램에는 프론트 엔드 및 백엔드 개발이 포함됩니다. 1) DOM 운영 및 이벤트 처리와 관련된 TODO 목록 응용 프로그램을 구축하여 프론트 엔드 애플리케이션을 표시합니다. 2) Node.js를 통해 RESTFULAPI를 구축하고 Express를 통해 백엔드 응용 프로그램을 시연하십시오.

웹 개발에서 JavaScript의 주요 용도에는 클라이언트 상호 작용, 양식 검증 및 비동기 통신이 포함됩니다. 1) DOM 운영을 통한 동적 컨텐츠 업데이트 및 사용자 상호 작용; 2) 사용자가 사용자 경험을 향상시키기 위해 데이터를 제출하기 전에 클라이언트 확인이 수행됩니다. 3) 서버와의 진실한 통신은 Ajax 기술을 통해 달성됩니다.

보다 효율적인 코드를 작성하고 성능 병목 현상 및 최적화 전략을 이해하는 데 도움이되기 때문에 JavaScript 엔진이 내부적으로 작동하는 방식을 이해하는 것은 개발자에게 중요합니다. 1) 엔진의 워크 플로에는 구문 분석, 컴파일 및 실행; 2) 실행 프로세스 중에 엔진은 인라인 캐시 및 숨겨진 클래스와 같은 동적 최적화를 수행합니다. 3) 모범 사례에는 글로벌 변수를 피하고 루프 최적화, Const 및 Lets 사용 및 과도한 폐쇄 사용을 피하는 것이 포함됩니다.

Python은 부드러운 학습 곡선과 간결한 구문으로 초보자에게 더 적합합니다. JavaScript는 가파른 학습 곡선과 유연한 구문으로 프론트 엔드 개발에 적합합니다. 1. Python Syntax는 직관적이며 데이터 과학 및 백엔드 개발에 적합합니다. 2. JavaScript는 유연하며 프론트 엔드 및 서버 측 프로그래밍에서 널리 사용됩니다.

Python과 JavaScript는 커뮤니티, 라이브러리 및 리소스 측면에서 고유 한 장점과 단점이 있습니다. 1) Python 커뮤니티는 친절하고 초보자에게 적합하지만 프론트 엔드 개발 리소스는 JavaScript만큼 풍부하지 않습니다. 2) Python은 데이터 과학 및 기계 학습 라이브러리에서 강력하며 JavaScript는 프론트 엔드 개발 라이브러리 및 프레임 워크에서 더 좋습니다. 3) 둘 다 풍부한 학습 리소스를 가지고 있지만 Python은 공식 문서로 시작하는 데 적합하지만 JavaScript는 MDNWebDocs에서 더 좋습니다. 선택은 프로젝트 요구와 개인적인 이익을 기반으로해야합니다.

C/C에서 JavaScript로 전환하려면 동적 타이핑, 쓰레기 수집 및 비동기 프로그래밍으로 적응해야합니다. 1) C/C는 수동 메모리 관리가 필요한 정적으로 입력 한 언어이며 JavaScript는 동적으로 입력하고 쓰레기 수집이 자동으로 처리됩니다. 2) C/C를 기계 코드로 컴파일 해야하는 반면 JavaScript는 해석 된 언어입니다. 3) JavaScript는 폐쇄, 프로토 타입 체인 및 약속과 같은 개념을 소개하여 유연성과 비동기 프로그래밍 기능을 향상시킵니다.

각각의 엔진의 구현 원리 및 최적화 전략이 다르기 때문에 JavaScript 엔진은 JavaScript 코드를 구문 분석하고 실행할 때 다른 영향을 미칩니다. 1. 어휘 분석 : 소스 코드를 어휘 단위로 변환합니다. 2. 문법 분석 : 추상 구문 트리를 생성합니다. 3. 최적화 및 컴파일 : JIT 컴파일러를 통해 기계 코드를 생성합니다. 4. 실행 : 기계 코드를 실행하십시오. V8 엔진은 즉각적인 컴파일 및 숨겨진 클래스를 통해 최적화하여 Spidermonkey는 유형 추론 시스템을 사용하여 동일한 코드에서 성능이 다른 성능을 제공합니다.

실제 세계에서 JavaScript의 응용 프로그램에는 서버 측 프로그래밍, 모바일 애플리케이션 개발 및 사물 인터넷 제어가 포함됩니다. 1. 서버 측 프로그래밍은 Node.js를 통해 실현되며 동시 요청 처리에 적합합니다. 2. 모바일 애플리케이션 개발은 재교육을 통해 수행되며 크로스 플랫폼 배포를 지원합니다. 3. Johnny-Five 라이브러리를 통한 IoT 장치 제어에 사용되며 하드웨어 상호 작용에 적합합니다.


핫 AI 도구

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

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

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

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

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

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

PhpStorm 맥 버전
최신(2018.2.1) 전문 PHP 통합 개발 도구

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

ZendStudio 13.5.1 맥
강력한 PHP 통합 개발 환경
