Heim >Web-Frontend >js-Tutorial >Ausführliche Erläuterung der Verwendung von jQuery ohne Neukonstruktion

Ausführliche Erläuterung der Verwendung von jQuery ohne Neukonstruktion

php中世界最好的语言
php中世界最好的语言Original
2018-04-23 11:24:401662Durchsuche

Dieses Mal werde ich Ihnen die Verwendung von jQuery ohne neue Konstruktion ausführlich erklären. Was sind die Vorsichtsmaßnahmen bei der Verwendung von jQuery ohne neue Konstruktion?

jQuerys neue, freie Konstruktion

Der Kern des jQuery-Frameworks besteht darin, Elemente aus HTML-Dokumenten abzugleichen und Operationen an ihnen auszuführen,

Erinnern Sie sich an die Verwendung jQuery Bei Verwendung von jQuery lautet die Methode zum Instanziieren eines jQuery-Objekts wie folgt:

// 无 new 构造
$('#test').text('Test');
 
// 当然也可以使用 new
var test = new $('#test');
test.text('Test');

Wenn die meisten Benutzer jQuery verwenden, verwenden sie die erste Konstruktionsmethode ohne neue, direkte Konstruktion von $(''), was auch sehr wichtig ist jQuery. Ein bequemer Ort.

Wenn wir die erste neue Konstruktionsmethode verwenden, entspricht sie im Wesentlichen new jQuery(). Wie wird sie also in jQuery implementiert? Schauen Sie mal rein:

(function(window, undefined) {
  var
  // ...
  jQuery = function(selector, context) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init(selector, context, rootjQuery);
  },
 
  jQuery.fn = jQuery.prototype = {
    init: function(selector, context, rootjQuery) {
      // ...
    }
  }
  jQuery.fn.init.prototype = jQuery.fn;
})(window);

Nicht verstanden? Es spielt keine Rolle, wir werden es Schritt für Schritt analysieren.

Funktionsausdrücke und Funktionsdeklarationen

In ECMAScript sind die beiden häufigsten Methoden zum Erstellen von Funktionen Funktionsausdrücke und Funktionsdeklarationen, der Unterschied zwischen Das ist etwas verwirrend, da die ECMA-Spezifikation nur einen Punkt klarstellt: Die Funktionsdeklaration muss einen Bezeichner (Identifier) haben (was jeder oft als Funktionsnamen bezeichnet), während der Funktionsausdruck diesen Bezeichner weglassen kann:

   //函数声明:
  function 函数名称 (参数:可选){ 函数体 }
  //函数表达式:
  function 函数名称(可选)(参数:可选){ 函数体 }

Es ist also ersichtlich, dass es sich um einen Ausdruck handeln muss, wenn der Funktionsname nicht deklariert ist. Wenn der Funktionsname jedoch deklariert ist, wie kann festgestellt werden, ob dies der Fall ist? eine Funktionsdeklaration oder ein Funktionsausdruck? Was ist mit der Formel?

ECMAScript unterscheidet sich durch den Kontext. Wenn

als Teil eines Zuweisungsausdrucks verwendet wird, handelt es sich um einen Funktionsausdruck. function foo(){}

Wenn

in einem Funktionskörper enthalten ist Ganz oben im Programm handelt es sich um eine Funktionsdeklaration. function foo(){}

 function foo(){} // 声明,因为它是程序的一部分
 var bar = function foo(){}; // 表达式,因为它是赋值表达式的一部分
 new function bar(){}; // 表达式,因为它是new表达式
 (function(){
  function bar(){} // 声明,因为它是函数体的一部分
 })();
Es gibt auch einen weniger verbreiteten Funktionsausdruck, der

in Klammern eingeschlossen ist. Der Grund, warum es sich um einen Ausdruck handelt, liegt darin, dass die Klammern () ein Gruppierungsoperator sind und nur intern möglich sind enthalten Ausdrücke (function foo(){})

Schauen wir uns den jQuery-Quellcode an:

(function(window, undefined) {
  /...
})(window)
Die obige Codestruktur kann in zwei Teile unterteilt werden:

und :(function(){window, undefined}) (window) ,

Kapitel 1 () ist ein Ausdruck, und der Ausdruck selbst ist eine anonyme Funktion,

. Das Hinzufügen von (window) nach diesem Ausdruck bedeutet also, dass die anonyme Funktion ausgeführt und ein Parameterfenster übergeben wird .

Prototyp Prototyp

Wissen Sie, was ein Prototyp ist?

In JavaScript ist der

-Prototyp auch ein Objekt. Durch den Prototyp kann die Attributvererbung des Objekts realisiert werden. JavaScript-Objekte enthalten alle ein -internes Attribut Der Prototyp dieses Objekts. " [[Prototype]]"

Manchmal können die beiden Attribute „Prototyp“ und „Proto“ verwechselt werden. „Person.prototype“ und „Person.proto“ sind völlig unterschiedlich.

Hier eine kurze Einführung zu „Prototyp“ und „Proto“:

1. Für alle Objekte gibt es ein Proto-Attribut, das dem Prototyp des Objekts entspricht

2. Für Funktionsobjekte gibt es zusätzlich zum Proto-Attribut auch ein Prototyp-Attribut

Wenn eine Funktion als Konstruktor zum Erstellen einer Instanz verwendet wird, wird der Prototyp-Attributwert des Funktion wird als Prototyp-Zuweisung für alle Objektinstanzen verwendet (d. h. Festlegen des Proto-Attributs der Instanz)

function Person(name, age){
  this.name = name;
  this.age = age;
}
Person.prototype.getInfo = function(){
  console.log(this.name + " is " + this.age + " years old");
};
//调用
var will = new Person("Will", 28);
will.getInfo();//"Will is 28 years old"

Abschluss

Definition des Abschlusses:

Ein Abschluss wird gebildet, wenn eine

innere Funktion durch eine Variable außerhalb ihrer äußeren Funktion referenziert wird.

Die Rolle des Abschlusses:

Bevor wir die Rolle des Abschlusses verstehen, wollen wir zunächst den GC-Mechanismus in Javascript verstehen:

In Javascript,

Wenn auf ein Objekt nicht mehr verwiesen wird, wird das Objekt von GC recycelt, andernfalls wird das Objekt immer im Speicher gespeichert.

在上述例子中,B定义在A中,因此B依赖于A,而外部变量 c 又引用了B, 所以A间接的被 c 引用,

也就是说,A不会被GC回收,会一直保存在内存中。为了证明我们的推理,看如下例子:

function A(){
  var count = 0;
  function B(){
    count ++;
    console.log(count);
  }
  return B;
}
var c = A();
c();// 1
c();// 2
c();// 3

count是A中的一个变量,它的值在B中被改变,函数B每执行一次,count的值就在原来的基础上累加1。因此,A中的count一直保存在内存中。

这就是闭包的作用,有时候我们需要一个模块中定义这样一个变量:希望这个变量一直保存在内存中但又不会“污染”全局的变量,这个时候,我们就可以用闭包来定义这个模块

在看jQuery源码:

(function(window, undefined) {
  var
  // ...
  jQuery = function(selector, context) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init(selector, context, rootjQuery);
  },
  jQuery.fn = jQuery.prototype = {
    init: function(selector, context, rootjQuery) {
      // ...
    }
  }
  jQuery.fn.init.prototype = jQuery.fn;
})(window);

我们知道了 什么是闭包:当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。

jQuery.fn的init 函数被jQuery 的构造函数调用了,这里形成了一个闭包。
构造函数及调用代码:

// ...
  jQuery = function(selector, context) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init(selector, context, rootjQuery);
  },

问题关键来了。

如何实现无new构建

JavaScript是函数式语言,函数可以实现类,类就是面向对象编程中最基本的概念

var aQuery = function(selector, context) {
    //构造函数
}
aQuery.prototype = {
  //原型
  name:function(){},
  age:function(){}
}
var a = new aQuery();
a.name();

这是常规的使用方法,显而易见jQuery不是这样玩的

要实现这样,那么jQuery就要看成一个类,那么$()应该是返回类的实例才对

按照jQuery的抒写方式

$().ready() 
$().noConflict()

要实现这样,那么jQuery就要看成一个类,那么$()应该是返回类的实例才对

所以把代码改一下:

var aQuery = function(selector, context) {
    return new aQuery();
}
aQuery.prototype = {
  name:function(){},
  age:function(){}
}

通过new aQuery(),虽然返回的是一个实例,但是也能看出很明显的问题,死循环了

那么如何返回一个正确的实例?

在javascript中实例this只跟原型有关系

那么可以把jQuery类当作一个工厂方法来创建实例,把这个方法放到aQuery.prototye原型中

var aQuery = function(selector, context) {
    return aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init:function(selector){
    return this;
  }
  name:function(){},
  age:function(){}
}

当执行aQuery() 返回的实例:

很明显aQuery()返回的是aQuery类的实例,那么在init中的this其实也是指向的aQuery类的实例

问题来了init的this指向的是aQuery类,如果把init函数也当作一个构造器,那么内部的this要如何处理?

var aQuery = function(selector, context) {
    return aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init: function(selector) {
    this.age = 18
    return this;
  },
  name: function() {},
  age: 20
}
aQuery().age //18

 因为this只是指向aQuery类的,所以aQueryage属性是可以被修改的。

这样看似没有问题,其实问题很大的

为什么是new jQuery.fn.init?

看如下代码:

var aQuery = function(selector, context) {
    return aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init: function(selector) {
    if(selector=="a")
      this.age = 18
    return this;
  },
  name: function() {},
  age: 20
}
aQuery("a").age //18
aQuery("b").age //18

当我调用 传入"a"的时候,修改age=18,及aQuery("a").age 的值为18

但是当我  传入"b"的时候 并没又修改 age的值,我也希望得到默认age的值20,但是aQuery("b").age 的值为18.

因为在 调用aQuery("a").age 的时候age被修改了。

这样的情况下就出错了,所以需要设计出独立的作用域才行。

jQuery框架分隔作用域的处理

jQuery = function( selector, context ) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init( selector, context, rootjQuery );
  },

很明显通过实例init函数,每次都构建新的init实例对象,来分隔this,避免交互混淆

我们修改一下代码:

var aQuery = function(selector, context) {
    return new aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init: function(selector) {
    if(selector=="a")
      this.age = 18
    return this;
  },
  name: function() {},
  age: 20
}
aQuery("a").age //18
aQuery("b").age //undefined
aQuery("a").name() //Uncaught TypeError: Object [object Object] has no method 'name'

又出现一个新的问题,

age  :undefined,

name() :抛出错误,无法找到这个方法,所以很明显new的init跟jquery类的this分离了

怎么访问jQuery类原型上的属性与方法?

     做到既能隔离作用域还能使用jQuery原型对象的作用域呢,还能在返回实例中访问jQuery的原型对象?

实现的关键点

// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;

我们再改一下:

var aQuery = function(selector, context) {
    return new aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init: function(selector) {
    if(selector=="a")
      this.age = 18
    return this;
  },
  name: function() {
     return age;
  },
  age: 20
}
aQuery.prototype.init.prototype = aQuery.prototype; 
aQuery("a").age //18
aQuery("b").age //20
aQuery("a").name()  //20

最后在看一下jQuery源码:

(function(window, undefined) {
  var
  // ...
  jQuery = function(selector, context) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init(selector, context, rootjQuery);
  },
  jQuery.fn = jQuery.prototype = {
    init: function(selector, context, rootjQuery) {
      // ...
    }
  }
  jQuery.fn.init.prototype = jQuery.fn;
})(window);

是不是明白了?

哈哈哈~~~

在简单说两句:

大部分人初看 jQuery.fn.init.prototype = jQuery.fn 这一句都会被卡主,很是不解。但是这句真的算是 jQuery 的绝妙之处。理解这几句很重要,分点解析一下:

1)首先要明确,使用 $('xxx') 这种实例化方式,其内部调用的是 return new jQuery.fn.init(selector, context, rootjQuery) 这一句话,也就是构造实例是交给了 jQuery.fn.init() 方法取完成。

2)将 jQuery.fn.init 的 prototype 属性设置为 jQuery.fn,那么使用 new jQuery.fn.init() 生成的对象的原型对象就是 jQuery.fn ,所以挂载到 jQuery.fn 上面的函数就相当于挂载到 jQuery.fn.init() 生成的 jQuery 对象上,所有使用 new jQuery.fn.init() 生成的对象也能够访问到 jQuery.fn 上的所有原型方法。

3)也就是实例化方法存在这么一个关系链 

    1.jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;

    2.new jQuery.fn.init() 相当于 new jQuery() ;

    3.jQuery() 返回的是 new jQuery.fn.init(),而 var obj = new jQuery(),所以这 2 者是相当的,所以我们可以无 new 实例化 jQuery 对象。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

jquery总体架构分析与使用详解

jQuery判断上传图片类型与大小方法详解

Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung der Verwendung von jQuery ohne Neukonstruktion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn