- 浏览: 1336520 次
- 性别:
- 来自: 成都
文章分类
- 全部博客 (471)
- 原创文章 (4)
- Database (84)
- J2SE (63)
- Web (26)
- Javascript (30)
- Lucene (11)
- os (13)
- 算法 (8)
- Webservice (1)
- Open projects (18)
- Hibernate (18)
- Spring (15)
- Css (2)
- J2ee (2)
- 综合技术 (18)
- 安全管理 (13)
- PatternsInJava (27)
- NIO (5)
- Ibatis (2)
- 书籍收藏 (1)
- quartz (7)
- 并发编程 (15)
- oracle问题 (2)
- ios (60)
- coco2d-iphone (3)
- C++ (6)
- Zookeeper (2)
- golang (4)
- animation (2)
- android (1)
最新评论
-
dandingge123:
【引用】限制UITextField输入长度的方法 -
qja:
...
对List顺序,逆序,随机排列实例代码 -
安静听歌:
现在在搞这个,,,,,哎~头都大了,,,又freemarker ...
通用大型网站页面静态化解决方案(一) -
springdata-jpa:
java quartz定时任务demo教程源代码下载,地址:h ...
Quartz 配置参考 -
马清天:
[b][/b][list][*]引用[u][/u][/list ...
通用大型网站页面静态化解决方案(一)
做网站免不了用到JS,如果大量用到选择一个JS框架是个不错的选择,今天我就抛砖引玉说说jQuery的选择器,了解它的原理是提高效率的最佳途径。每当你看到jQuery独特的语法时是否有想过它是如何实现的呢?是否觉得相当复杂让你都不敢去想呢,今天就让我们揭开jQuery选择器的神秘面纱。
一、选择器入口
var Utils = {
find : function (expr,context){
return selector.quick(expr,context || document);
}
}
expr就是表达式了,如下(注:我所说的都是jQuery的原理):
代码
//效率最高,直接映射到document.getElementById
Utils.find("#id")
//支持querySelectorAll否则调用context.getElementsByName然后遍历getAttribute("name")等于a的
Utils.find("input[name=a]")
//不支持querySelectorAll,调用context.getElementsByName然后遍历getAttribute("name")等于a的,然后筛选出选中的
Utils.find("input:checked[name=a]")
//支持querySelectorAll否则映射到context.getElementsByTagName
Utils.find("input")
//支持querySelectorAll否则调用context.getElementsByTagName("*")然后筛选出className包含className的标签
Utils.find(".cssName")
context 上下文哦,主要是在这个里面查找哦
代码
var selector = {};
selector.quick = function(expr,context){
//#id
if( /^#([\w-]+)$/.test( expr ) ){
return context.getElementById( expr.substr(1) );
}
//TAG
else if ( /^\w+$/.test( expr ) ) {
return selector.array( context.getElementsByTagName( expr ));
}
try
{
return selector.array(context.querySelectorAll(expr));//ie6 ie7都不支持哦
}catch(e) {}
return selector.array(selector(expr,context,isXML(context)));//不支持就只能用最原始的啦
}
querySelectorAll
是什么?这个可是主角啊,jQuery独特的语法其实就是参考自它的,标准且主流的浏览器都支持了,比如ie8,ff等等,然而ie6、ie7都不支持哦,所以我们需要自己来实现,后面很多代码其实都是为了解决不支持它而写的,可恨啊。。。
查看资料:
http://www.w3.org/TR/selectors-api/
http://www.w3.org/TR/css3-selectors/
http://msdn.microsoft.com/zh-cn/library/cc304114(en-us,VS.85).aspx
看看document.querySelectorAll都支持什么语法
代码
var a = document.querySelectorAll("#a, #b, #c");
var b = document.querySelectorAll("#frm p #a");
var c = document.querySelectorAll("body p.cssName");
var d = document.querySelectorAll("p.abc, #a");
var e = document.querySelectorAll("input");
var f = document.querySelector("body p[id=aa] span");
var g = document.querySelectorAll("ul.nav>li");
var h = document.querySelectorAll("input[checked=checked]");
//.....
二、选择器实现
代码
var selector = function(expr, context, isXML){
var set, match;
if ( !expr ) {
return [];
}
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var type = Expr.order[i], match;
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
var left = match[1];
match.splice(1,1);
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace(/\\/g, "");
set = Expr.find[ type ]( match, context, isXML );
if ( set != null ) {
expr = expr.replace( Expr.match[ type ], "" );
break;
}
}
}
}
if ( !set ) {
set =
context.getElementsByTagName("*");//效率非常低下,获取所有的标签,然后调用filter筛选出需要的
}
return filter(expr,set);
};
selector.array = function(array){
var ret = [];
if ( Object.prototype.toString.call(array) === "[object Array]" ) {
Array.prototype.push.apply( ret, array );
} else {
if ( typeof array.length === "number" ) {
for ( var i = 0, l = array.length; i < l; i++ ) {
ret.push( array[i] );
}
} else {
for ( var i = 0; array[i]; i++ ) {
ret.push( array[i] );
}
}
}
return ret;
}
//判断是否是xml
var isXML = function(elem){
// documentElement is verified for cases where it doesn't yet exist
// (such as loading iframes in IE - #4833)
var documentElement = (elem ? elem.ownerDocument || elem :
0).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
其实上面都实现大半了,下面会有很多代码其实都是为了解决不支持querySelectorAll 而写的。看代码吧,其实不难。
代码
var Expr = {
order: [ "ID", "NAME", "TAG" ],
match: {
ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
ATTR:
/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
PSEUDO:
/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
},
leftMatch: {},
attrMap: {
"class": "className",
"for": "htmlFor"
},
attrHandle: {
href: function(elem){
return elem.getAttribute("href");
}
},
relative: {
"+": function(checkSet, part){
var isPartStr = typeof part === "string",
isTag = isPartStr && !/\W/.test(part),
isPartStrNotTag = isPartStr && !isTag;
if ( isTag ) {
part = part.toLowerCase();
}
for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
if ( (elem = checkSet[i]) ) {
while ( (elem = elem.previousSibling) && elem.nodeType !== 1
) {}
checkSet[i] = isPartStrNotTag || elem &&
elem.nodeName.toLowerCase() === part ?
elem || false :
elem === part;
}
}
if ( isPartStrNotTag ) {
Sizzle.filter( part, checkSet, true );
}
},
">": function(checkSet, part){
var isPartStr = typeof part === "string";
if ( isPartStr && !/\W/.test(part) ) {
part = part.toLowerCase();
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
var parent = elem.parentNode;
checkSet[i] = parent.nodeName.toLowerCase() === part ?
parent : false;
}
}
} else {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
checkSet[i] = isPartStr ?
elem.parentNode :
elem.parentNode === part;
}
}
if ( isPartStr ) {
Sizzle.filter( part, checkSet, true );
}
}
},
"": function(checkSet, part, isXML){
var doneName = done++, checkFn = dirCheck;
if ( typeof part === "string" && !/\W/.test(part) ) {
var nodeCheck = part = part.toLowerCase();
checkFn = dirNodeCheck;
}
checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
},
"~": function(checkSet, part, isXML){
var doneName = done++, checkFn = dirCheck;
if ( typeof part === "string" && !/\W/.test(part) ) {
var nodeCheck = part = part.toLowerCase();
checkFn = dirNodeCheck;
}
checkFn("previousSibling", part, doneName, checkSet, nodeCheck,
isXML);
}
},
find: {
ID: function(match, context, isXML){
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ? [m] : [];
}
},
NAME: function(match, context){
if ( typeof context.getElementsByName !== "undefined" ) {
var ret = [], results = context.getElementsByName(match[1]);
for ( var i = 0, l = results.length; i < l; i++ ) {
if ( results[i].getAttribute("name") === match[1] ) {
ret.push( results[i] );
}
}
return ret.length === 0 ? null : ret;
}
},
TAG: function(match, context){
return context.getElementsByTagName(match[1]);
}
},
preFilter: {
CLASS: function(match, curLoop, inplace, result, not, isXML){
match = " " + match[1].replace(/\\/g, "") + " ";
if ( isXML ) {
return match;
}
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
if ( not ^ (elem.className && (" " + elem.className + "
").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
if ( !inplace ) {
result.push( elem );
}
} else if ( inplace ) {
curLoop[i] = false;
}
}
}
return false;
},
ID: function(match){
return match[1].replace(/\\/g, "");
},
TAG: function(match, curLoop){
return match[1].toLowerCase();
},
CHILD: function(match){
if ( match[1] === "nth" ) {
// parse equations like 'even', 'odd', '5', '2n', '3n+2',
'4n-1', '-n+6'
var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
match[2] === "even" && "2n" || match[2] === "odd" && "2n+1"
||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
// calculate the numbers (first)n+(last) including if they are
negative
match[2] = (test[1] + (test[2] || 1)) - 0;
match[3] = test[3] - 0;
}
// TODO: Move to normal caching system
match[0] = done++;
return match;
},
ATTR: function(match, curLoop, inplace, result, not, isXML){
var name = match[1].replace(/\\/g, "");
if ( !isXML && Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
}
if ( match[2] === "~=" ) {
match[4] = " " + match[4] + " ";
}
return match;
},
PSEUDO: function(match, curLoop, inplace, result, not){
if ( match[1] === "not" ) {
// If we're dealing with a complex expression, or a simple one
if ( ( chunker.exec(match[3]) || "" ).length > 1 ||
/^\w/.test(match[3]) ) {
match[3] = Sizzle(match[3], null, null, curLoop);
} else {
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^
not);
if ( !inplace ) {
result.push.apply( result, ret );
}
return false;
}
} else if ( Expr.match.POS.test( match[0] ) ||
Expr.match.CHILD.test( match[0] ) ) {
return true;
}
return match;
},
POS: function(match){
match.unshift( true );
return match;
}
},
filters: {
enabled: function(elem){
return elem.disabled === false && elem.type !== "hidden";
},
disabled: function(elem){
return elem.disabled === true;
},
checked: function(elem){
return elem.checked === true;
},
selected: function(elem){
// Accessing this property makes selected-by-default
// options in Safari work properly
elem.parentNode.selectedIndex;
return elem.selected === true;
},
parent: function(elem){
return !!elem.firstChild;
},
empty: function(elem){
return !elem.firstChild;
},
has: function(elem, i, match){
return !!Sizzle( match[3], elem ).length;
},
header: function(elem){
return /h\d/i.test( elem.nodeName );
},
text: function(elem){
return "text" === elem.type;
},
radio: function(elem){
return "radio" === elem.type;
},
checkbox: function(elem){
return "checkbox" === elem.type;
},
file: function(elem){
return "file" === elem.type;
},
password: function(elem){
return "password" === elem.type;
},
submit: function(elem){
return "submit" === elem.type;
},
image: function(elem){
return "image" === elem.type;
},
reset: function(elem){
return "reset" === elem.type;
},
button: function(elem){
return "button" === elem.type || elem.nodeName.toLowerCase() ===
"button";
},
input: function(elem){
return /input|select|textarea|button/i.test(elem.nodeName);
}
},
setFilters: {
first: function(elem, i){
return i === 0;
},
last: function(elem, i, match, array){
return i === array.length - 1;
},
even: function(elem, i){
return i % 2 === 0;
},
odd: function(elem, i){
return i % 2 === 1;
},
lt: function(elem, i, match){
return i < match[3] - 0;
},
gt: function(elem, i, match){
return i > match[3] - 0;
},
nth: function(elem, i, match){
return match[3] - 0 === i;
},
eq: function(elem, i, match){
return match[3] - 0 === i;
}
},
filter: {
PSEUDO: function(elem, match, i, array){
var name = match[1], filter = Expr.filters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
} else if ( name === "contains" ) {
return (elem.textContent || elem.innerText || getText([ elem ])
|| "").indexOf(match[3]) >= 0;
} else if ( name === "not" ) {
var not = match[3];
for ( var i = 0, l = not.length; i < l; i++ ) {
if ( not[i] === elem ) {
return false;
}
}
return true;
} else {
Sizzle.error( "Syntax error, unrecognized expression: " + name
);
}
},
CHILD: function(elem, match){
var type = match[1], node = elem;
switch (type) {
case 'only':
case 'first':
while ( (node = node.previousSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
if ( type === "first" ) {
return true;
}
node = elem;
case 'last':
while ( (node = node.nextSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
return true;
case 'nth':
var first = match[2], last = match[3];
if ( first === 1 && last === 0 ) {
return true;
}
var doneName = match[0],
parent = elem.parentNode;
if ( parent && (parent.sizcache !== doneName ||
!elem.nodeIndex) ) {
var count = 0;
for ( node = parent.firstChild; node; node =
node.nextSibling ) {
if ( node.nodeType === 1 ) {
node.nodeIndex = ++count;
}
}
parent.sizcache = doneName;
}
var diff = elem.nodeIndex - last;
if ( first === 0 ) {
return diff === 0;
} else {
return ( diff % first === 0 && diff / first >= 0 );
}
}
},
ID: function(elem, match){
return elem.nodeType === 1 && elem.getAttribute("id") === match;
},
TAG: function(elem, match){
return (match === "*" && elem.nodeType === 1) ||
elem.nodeName.toLowerCase() === match;
},
CLASS: function(elem, match){
return (" " + (elem.className || elem.getAttribute("class")) + " ")
.indexOf( match ) > -1;
},
ATTR: function(elem, match){
var name = match[1],
result = Expr.attrHandle[ name ] ?
Expr.attrHandle[ name ]( elem ) :
elem[ name ] != null ?
elem[ name ] :
elem.getAttribute( name ),
value = result + "",
type = match[2],
check = match[4];
return result == null ?
type === "!=" :
type === "=" ?
value === check :
type === "*=" ?
value.indexOf(check) >= 0 :
type === "~=" ?
(" " + value + " ").indexOf(check) >= 0 :
!check ?
value && result !== false :
type === "!=" ?
value !== check :
type === "^=" ?
value.indexOf(check) === 0 :
type === "$=" ?
value.substr(value.length - check.length) === check :
type === "|=" ?
value === check || value.substr(0, check.length + 1) === check +
"-" :
false;
},
POS: function(elem, match, i, array){
var name = match[2], filter = Expr.setFilters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
}
}
}
};
for ( var type in Expr.match ) {
Expr.match[ type ] = new RegExp( Expr.match[ type ].source +
/(?![^\[]*\])(?![^\(]*\))/.source );
Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[
type ].source.replace(/\\(\d+)/g, function(all, num){
return "\\" + (num - 0 + 1);
}));
}
jQuery 不是获取的多个元素可以each遍历么?看看如何实现哦
代码
if (!Object.prototype.each) {
Array.prototype.each = function (fn) {
if ( this.length > 0 ) {
for ( var i = 0; i < this.length; i++ ) {
fn.call( this[i], i );
}
}
}
}
发表评论
-
关闭输入框自动完成 firefox msie disable input autocomplete
2010-10-28 10:43 3131English Title:Shut input casing ... -
js中prototype用法
2010-10-20 15:56 1278prototype 是在 IE 4 及其以后版本引入的一个 ... -
jquery source
2010-10-20 15:03 1247http://tech.ddvip.com/2009-01/1 ... -
使用 JavaScript 拦截和跟踪浏览器中的 HTTP 请求
2010-10-20 14:10 3825HTTP 请求的拦截技术可以广泛地应用在反向代理、拦截 Aja ... -
监听输入框值的即时变
2010-10-20 12:34 1642写道 <html> <head> ... -
判断上传文件的大小
2010-10-19 12:44 1369var Sys = {}; if ... -
js继承的实现
2010-10-19 11:16 1151写道 js继承有5种实现方式: 1、继承第一种方式:对象 ... -
js面向对象
2010-10-19 11:15 2575写道 1、js区别于java的特性还有:实例化类对象时,如 ... -
js高级进阶
2010-10-19 11:14 8168写道 1、js不支持函数(方法)的重载 2、js中,一个 ... -
jQuery 简但实现select二级联动
2010-08-23 12:09 4773写道 < !DOCTYPE html PUBLIC ... -
原始的Ajax 直接使用XmlHttpRequest
2010-08-23 11:29 1908写道 //定义XMLHttp实例 var xmlHttp ... -
jquery checkbox,radio ,select value
2010-08-12 10:01 1696写道 .date-region select:visi ... -
jquery flot 使用笔记
2010-06-27 00:57 5812写道 <!DOCTYPE HTML PUBLIC &q ... -
js clone
2010-05-13 13:50 1290<!DOCTYPE html PUBLIC &q ... -
javascript可以轻松操作客户端剪贴板内容
2010-04-19 12:29 1364写道 <html> <head> ... -
dhtml
2010-04-09 10:18 2381写道 Object.__defineGetter__ = ... -
MzTreeView
2010-03-24 16:05 5673写道 MzTreeView 1.0 是数据一次性加载,客户 ... -
JavaScript 节点操作 以及DOMDocument属性和方法
2010-03-24 15:28 16301Attributes 存储节点的属 ... -
在网页里打开本地的驱动器
2010-03-24 12:14 1604<html> <head> < ... -
页面登录进度条
2010-03-24 12:13 1634<form name=loading> < ...
相关推荐
**jQuery 实现颜色选择器** 在网页开发中,颜色选择器是一个常见的交互元素,它允许用户方便地选取颜色。在本教程中,我们将探讨如何利用 jQuery 和相关插件来实现一个功能完备的颜色选择器。 首先,`jQuery` 是一...
这个名为"jQuery选择器.rar"的压缩包文件显然包含了关于jQuery选择器的相关学习资源,可能包括示例代码、教程文档等。这些内容可能与《锋利的jQuery第二版》这本书中的章节相匹配,该书是jQuery学习的经典参考资料之...
通过熟练掌握这些jQuery选择器,你可以更高效地选取和操作DOM元素,实现更复杂的交互和动态效果。在实际项目中,结合使用选择器,结合jQuery的其他功能,如事件处理和动画效果,可以极大地提升开发效率和代码质量。...
为了进一步学习和理解,你可以解压文件,查看其中的JavaScript代码,分析它是如何使用jQuery选择器进行双向操作的。此外,也可以查阅jQuery官方文档和在线教程,以获取更全面的知识和实践指导。 总的来说,掌握...
### jQuery选择器 jQuery支持多种选择器,扩展了CSS的选择能力: 1. **基本选择器**:`$('element')`(标签选择器)、`$('#id')`(ID选择器)、`$('.class')`(类选择器)。 2. **组合选择器**:`$('element1, ...
jQuery选择器可以灵活组合,实现复杂的选择需求。例如,`$("div.myClass#myId input[type='text']")`将选取id为"myId"且类名为"myClass"的div内的所有文本输入框。 通过掌握以上jQuery选择器的用法,你可以更加精确...
标题中的“jquery 城市三字码 选择器”是指使用jQuery库开发的一个功能,它允许用户在前端界面中选择城市,同时返回该城市的三字码。在国际航空运输协会(IATA)的标准中,每个城市都有一个三位字母的代码,方便在...
- **The jQuery Wrapper**:当使用 JQuery 选择器选择 DOM 元素时,返回的是一个称为“JQuery 对象”的封装对象,这个对象提供了丰富的 API 来操作 DOM。 - **Utility Functions**:JQuery 提供了一系列实用函数,...
CSS选择器可能在某些浏览器中不兼容,而jQuery选择器提供了更好的跨浏览器兼容性。 - 例如,CSS的子选择器`#box > p`在IE6中可能不工作,但jQuery的`$('#box > p').css('color', 'red')`则能兼容IE6。 5. **jQuery...
我们可以创建一个名为`$.fn.monthPicker`的方法,这个方法将会扩展jQuery对象,使得任何jQuery选择器都能调用它。在函数内部,我们需要初始化插件的状态,例如当前选中的年份和月份,并为元素绑定事件监听器,如点击...
这个学习教程将引导你深入理解jQuery选择器的强大与灵活性,帮助你在最短时间内提升JavaScript开发效率。** jQuery选择器分为基本选择器、层级选择器、属性选择器、伪类选择器和组合选择器五大部分。 1. **基本...
首先,`jquery.miniColors.css`是插件的主题样式文件,包含了颜色选择器的布局和样式定义。在网页中引入此CSS文件,可以确保颜色选择器的外观与网页设计保持一致。文件中可能包括了颜色选择面板的尺寸、背景色、按钮...
**jQuery选择器详解** 在Web开发中,jQuery库极...为了深入学习jQuery选择器,你可以参考压缩包文件中的`ch06-jQuery选择器`资源,其中可能包含详细的讲解和示例代码,通过实践操作,你将更好地理解和应用这些选择器。
- `jQuery选择器.doc` 文件可能详细介绍了jQuery的各种选择器,包括基础选择器(如ID、类、元素名)、层次选择器(如后代、子元素、相邻兄弟、后续兄弟)、属性选择器以及表单选择器等。理解并熟练运用选择器可以...
jQuery选择器的强大之处在于它们可以组合使用,以实现复杂的元素选取。例如,选取类名为`highlight`并且`data-role`属性以`'link'`开头的所有元素: ```javascript $("[data-role^='link'].highlight").doSomething...
2. jQuery选择器:jQuery提供了丰富的选择器,如ID选择器(#id),类选择器(.class),元素选择器(element),以及组合选择器等,用于快速定位DOM元素。 二、DOM操作 3. 获取元素:$.fn.find()、$.fn.children()、$.fn...
1. **jQuery选择器**:jQuery提供了丰富的选择器,如ID选择器(#id)、类选择器(.class)、元素选择器(tagname)等,以及组合选择器,使得选取DOM元素变得更加便捷。 2. **DOM操作**:jQuery封装了对DOM的操作,如`$...
2. **jQuery选择器**:jQuery支持CSS选择器,如`id`、`class`、`tag`等,用于选取DOM元素。例如,选取ID为"myElement"的元素: ```javascript $("#myElement"); ``` 3. **DOM操作**:jQuery提供了一系列方法来...
2. **选择器**:了解jQuery的各种选择器,如ID选择器(`#id`)、类选择器(`.class`)、元素选择器(`element`)等,以及如何组合使用它们。 3. **链式操作**:理解jQuery对象的链式调用,如何在一个方法调用后立即执行另...
同时,遵循最佳实践,如避免过度使用jQuery选择器、优先使用原生JavaScript等功能,有助于构建更加高效、健壮的前端系统。 #### 结语 通过本教程的学习,读者不仅能够掌握jQuery的基础知识和核心功能,还能深入...