JavaScript에서 new 키워드를 사용하지 않는 이유에 대한 자세한 설명
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();
- 기존 메소드의 "클래스"는 생성자여야 합니다.
- 속성과 메소드는 프로토타입 속성에 바인딩되며 상속은 프로토타입 특성의 도움으로 구현됩니다.
- 객체를 인스턴스화하려면 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");
}
// 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

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

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

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

Clothoff.io
AI 옷 제거제

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

인기 기사

뜨거운 도구

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

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

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

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

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

뜨거운 주제











WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 오늘날의 디지털 시대에는 점점 더 많은 기업과 서비스에서 온라인 예약 기능을 제공해야 합니다. 효율적인 실시간 온라인 예약 시스템을 구현하는 것이 중요합니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. WebSocket이란 무엇입니까? WebSocket은 단일 TCP 연결의 전이중 방식입니다.

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 소개: 인터넷의 대중화와 기술의 발전으로 점점 더 많은 레스토랑에서 온라인 주문 서비스를 제공하기 시작했습니다. 실시간 온라인 주문 시스템을 구현하기 위해 JavaScript 및 WebSocket 기술을 사용할 수 있습니다. WebSocket은 TCP 프로토콜을 기반으로 하는 전이중 통신 프로토콜로 클라이언트와 서버 간의 실시간 양방향 통신을 실현할 수 있습니다. 실시간 온라인 주문 시스템에서는 사용자가 요리를 선택하고 주문을 하면

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

JavaScript에서 HTTP 상태 코드를 얻는 방법 소개: 프런트 엔드 개발에서 우리는 종종 백엔드 인터페이스와의 상호 작용을 처리해야 하며 HTTP 상태 코드는 매우 중요한 부분입니다. HTTP 상태 코드를 이해하고 얻는 것은 인터페이스에서 반환된 데이터를 더 잘 처리하는 데 도움이 됩니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. HTTP 상태 코드란 무엇입니까? HTTP 상태 코드는 브라우저가 서버에 요청을 시작할 때 서비스가

사용법: JavaScript에서 insertBefore() 메서드는 DOM 트리에 새 노드를 삽입하는 데 사용됩니다. 이 방법에는 삽입할 새 노드와 참조 노드(즉, 새 노드가 삽입될 노드)라는 두 가지 매개 변수가 필요합니다.
