>  기사  >  웹 프론트엔드  >  새로운 구성 없이 jQuery를 사용하는 방법에 대한 자세한 설명

새로운 구성 없이 jQuery를 사용하는 방법에 대한 자세한 설명

php中世界最好的语言
php中世界最好的语言원래의
2018-04-23 11:24:401583검색

이번에는 새로운 구성 없이 jQuery를 사용하는 방법에 대해 자세히 설명하겠습니다. 새로운 구성 없이 jQuery를 사용할 때의 주의사항은 무엇인가요?

jQuery의 새로운 무료 구성

jQuery 프레임워크의 핵심은 HTML 문서의 요소를 일치시키고 해당 요소에 대해 작업을 수행하는 것입니다.

jQuery를 사용할 때 jQuery 개체를 인스턴스화하는 방법을 기억하세요.

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

대부분의 사람들이 jQuery를 사용할 때 항상 새로운 생성 없이 첫 번째 생성 방법을 사용하여 직접 $('') 생성을 하는데, 이는 jQuery에 매우 편리한 위치이기도 합니다. $('') 进行构造,这也是 jQuery 十分便捷的一个地方。

当我们使用第一种无 new 构造方式的时候,其本质就是相当于 new jQuery(),那么在 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);

 没看懂?没关系,我们一步一步分析。

函数表达式和函数声明

在ECMAScript中,创建函数的最常用的两个方法是函数表达式和函数声明,两者期间的区别是有点晕,因为ECMA规范只明确了一点:函数声明必须带有标示符(Identifier)(就是大家常说的函数名称),而函数表达式则可以省略这个标示符: 

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

所以,可以看出,如果不声明函数名称,它肯定是表达式,可如果声明了函数名称的话,如何判断是函数声明还是函数表达式呢?

ECMAScript是通过上下文来区分的,如果function foo(){}是作为赋值表达式的一部分的话,那它就是一个函数表达式,

如果function foo(){}被包含在一个函数体内,或者位于程序的最顶部的话,那它就是一个函数声明。

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

还有一种函数表达式不太常见,就是被括号括住的(function foo(){}),他是表达式的原因是因为括号 ()是一个分组操作符,它的内部只能包含表达式

 再来看jQuery源码:

(function(window, undefined) {
  /...
})(window)

可以将上面的代码结构分成两部分:(function(){window, undefined}) (window) ,

第1个()是一个表达式,而这个表达式本身是一个匿名函数,

所以在这个表达式后面加(window)就表示执行这个匿名函数并传入参数window。

原型 prototype

认识一下什么是原型?

在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象中都包含了一个" [[Prototype]]"

첫 번째 new-less 생성 방법을 사용하면 본질적으로 new jQuery()와 동일합니다. 그러면 jQuery 내에서 어떻게 구현됩니까? 보세요:

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"
이해가 안 되시나요? 중요하지 않습니다. 단계별로 분석하겠습니다.

함수 표현식

및 함수 선언

ECMAScript에서 함수를 만드는 가장 일반적인 방법은 두 가지 메소드는 함수 표현식과 함수 선언입니다. ECMA 사양은 한 가지 사항만 명확하게 설명하기 때문에 둘 사이의 차이점은 다소 혼란스럽습니다. 즉, 함수 선언에는 식별자(Identifier)가 있어야 합니다(즉, 모든 사람이 흔히 함수 이름을 말함), 함수 표현식에서는 다음 식별자를 생략할 수 있습니다:

function A(){
  var count = 0;
  function B(){
    count ++;
    console.log(count);
  }
  return B;
}
var c = A();
c();// 1
c();// 2
c();// 3
따라서 함수 이름을 선언하려면 표현식이어야 하는데, 함수 이름을 선언하면 함수 선언인지 함수 표현식인지 어떻게 알 수 있나요?

ECMAScript는 문맥에 따라 구별됩니다. function foo(){}가 할당 표현식의 일부로 사용되면 이는 함수 표현식입니다. function foo( ){} 는 함수 본문이나 프로그램 상단에 포함된 경우 함수 선언입니다.

(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);

덜 일반적인 함수 표현식도 있는데, (function foo(){})가 괄호로 묶인 이유는 괄호()가 그룹화 기호이기 때문입니다. , 내부 표현식만 포함할 수 있습니다. jQuery 소스 코드를 다시 살펴보겠습니다.

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

위의 코드 구조를 : (function(){window, undefine})과 두 부분으로 나눌 수 있습니다. (window) ,

첫 번째 ()는 표현식이고 표현식 자체는 익명 함수입니다.

따라서 이 표현식 뒤에 (window)를 추가한다는 것은 이 익명 함수를 실행하고 전달한다는 의미입니다. 매개변수 창에서

프로토타입 프로토타입

🎜프로토타입이 무엇인지 알아볼까요? 🎜🎜JavaScript에서는 🎜프로토타입도 객체입니다. 프로토타입을 통해 객체의 속성 상속이 실현될 수 있습니다🎜. JavaScript 객체에는 모두 "[[Prototype]]"라는 내부 속성이 포함되어 있습니다. . 해당하는 것이 객체의 프로토타입입니다. 🎜🎜"prototype"과 "proto"라는 두 속성은 때때로 혼동될 수 있습니다. "Person.prototype"과 "Person.proto"는 완전히 다릅니다. 🎜🎜다음은 "prototype"과 "proto"에 대한 간략한 소개입니다: 🎜🎜 1. 모든 객체에는 객체의 프로토타입에 해당하는 proto 속성이 있습니다. 🎜🎜 2. 함수 객체의 경우, proto 속성 외에도 프로토타입 속성이 있습니다. 🎜함수를 🎜생성자🎜로 사용하여 인스턴스를 생성하면 해당 함수의 프로토타입 속성 값이 모든 객체 인스턴스에 프로토타입으로 할당됩니다. 인스턴스의 proto 속성) 🎜🎜
var aQuery = function(selector, context) {
    //构造函数
}
aQuery.prototype = {
  //原型
  name:function(){},
  age:function(){}
}
var a = new aQuery();
a.name();
🎜🎜Closure🎜🎜🎜🎜클로저 정의: 🎜🎜🎜 🎜내부 함수🎜가 외부 함수 외부의 변수에 의해 참조되면 클로저가 형성됩니다. 🎜🎜🎜클로저의 역할: 🎜🎜🎜클로저의 역할을 이해하기 전에 먼저 javascript의 GC 메커니즘을 이해해 보겠습니다. 🎜🎜javascript에서 🎜객체가 더 이상 참조되지 않으면 해당 객체는 GC 재활용이 됩니다. 그렇지 않으면 이 객체는 항상 메모리에 저장됩니다. 🎜🎜

在上述例子中,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判断上传图片类型与大小方法详解

위 내용은 새로운 구성 없이 jQuery를 사용하는 방법에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.